C++ 如何防止从某些代码段调用函数?

C++ 如何防止从某些代码段调用函数?,c++,c,c-preprocessor,C++,C,C Preprocessor,我正在实现一个helper类,它有许多有用的函数,这些函数将在大量的类中使用。然而,其中有一些并不是设计用来从某些代码段中调用的(从中断函数,这是一个嵌入式项目) 但是,对于此类用户来说,某些函数被允许而另一些函数被禁止从中断函数调用的原因可能不是很明显,在许多情况下,被禁止的函数可能会工作,但可能会导致以后非常微妙和难以发现的错误 对我来说,最好的解决方案是,如果从不应该从中调用的代码段调用有问题的函数,则会导致编译器错误 我也考虑过一些非技术性的解决方案,但最好是技术性的 在文档中用警告指示

我正在实现一个helper类,它有许多有用的函数,这些函数将在大量的类中使用。然而,其中有一些并不是设计用来从某些代码段中调用的(从中断函数,这是一个嵌入式项目)

但是,对于此类用户来说,某些函数被允许而另一些函数被禁止从中断函数调用的原因可能不是很明显,在许多情况下,被禁止的函数可能会工作,但可能会导致以后非常微妙和难以发现的错误

对我来说,最好的解决方案是,如果从不应该从中调用的代码段调用有问题的函数,则会导致编译器错误

我也考虑过一些非技术性的解决方案,但最好是技术性的

  • 在文档中用警告指示它。可能很容易被忽略,特别是当函数看起来很明显时,比如
    read_byte()
    ,为什么会有人研究文档中的函数是否可重入

  • 在函数名中指明它。丑陋的。谁喜欢函数名,比如从中断()读取字节不调用

  • 在每个文件的公共头中包含一个全局变量,在每个中断开始时设置为true,在结束时设置为false,有问题的函数在开始时检查它,如果设置了,则退出。问题:中断可能会相互中断。此外,它不会导致编译时警告或错误

  • 与#3类似,使用带有堆栈的全局处理程序,以便可以处理嵌套中断。仍然存在仅在运行时工作的问题,并且还增加了大量开销。中断不应该浪费超过一个或两个时钟周期的这个功能,如果有的话

  • 滥用预处理器。不幸的是,在每个中断的开头定义一个
    #define
    ,在每个中断的结尾定义一个
    #unde
    ,在有问题的函数的开头定义一个
    #ifdef
    ,这种天真的方法是行不通的,因为预处理器不关心作用域

  • 由于中断始终是无类函数,我可以使有问题的函数
    受到保护
    ,并在使用它们的所有类中将它们声明为
    朋友
    。这样,就不可能从中断中直接使用它们。由于
    main()
    是无类的,我必须将它的大部分放在类方法中。我不太喜欢这个,因为它可能变得不必要的复杂,并且它产生的错误并不明显(因此这个函数的用户可能会封装它们来“解决”问题,而没有意识到真正的问题是什么)。编译器或链接器错误消息(如“error:function_name()不能在中断中使用”)更可取

  • 检查函数中的中断寄存器有几个问题。在大型微控制器中,有许多寄存器需要检查。另外,当中断标志在一个时钟周期前被设置时,出现误报的可能性很小但很危险,因此我的函数将失败,因为它认为它是从中断调用的,而中断将在下一个周期调用。此外,在嵌套中断中,中断标志被清除,从而导致假阴性。最后,这是另一个运行时解决方案

  • 不久前,我确实玩过一些非常基本的模板元编程,但我没有经验找到一个非常简单和优雅的解决方案。在承诺实现模板元编程膨胀软件之前,我宁愿尝试其他方法


    仅使用C中可用功能的解决方案也是可以接受的,甚至更可取。

    如果所有中断例程都有一个文件,那么这可能会有帮助: 在类头中定义一个宏,比如禁止中断例程访问。 在中断处理程序文件中检查宏定义:

    #ifdef FORBID_INTERRUPT_ROUTINE_ACCESS
    #error : cannot access function from interrupt handlers.
    #endif
    
    如果有人为该类添加头文件,以便在中断处理程序中使用该类,那么它将抛出一个错误


    注意:必须通过指定警告将被视为错误来构建目标。

    如果所有中断例程都有一个文件,那么这可能会有帮助: 在类头中定义一个宏,比如禁止中断例程访问。 在中断处理程序文件中检查宏定义:

    #ifdef FORBID_INTERRUPT_ROUTINE_ACCESS
    #error : cannot access function from interrupt handlers.
    #endif
    
    如果有人为该类添加头文件,以便在中断处理程序中使用该类,那么它将抛出一个错误


    注意:您必须通过指定警告将被视为错误来构建目标。

    下面的一些注释。作为一个警告,阅读它们不会很有趣,但我不会不指出这里的问题而为你服务

    • 如果您是从ISR内部调用外部函数,那么任何文档或编码都无法帮助您。因为在大多数情况下,这样做是不好的做法。程序员必须知道他们在做什么,否则任何文档或编码机制都无法保存程序

      程序员设计库函数并不是为了从ISR内部调用。相反,程序员设计ISR:S中带有ISR的所有特殊限制:确保中断标志被正确清除,保持代码简短,不调用外部函数,不要比必要的时间更长地阻塞MCU,考虑重新入侵者,考虑危险的编译器优化(使用易失性)。不知道这一点的人不足以编写ISR

    • 如果您实际上有一个函数
      int read\u byte(int address)
      ,那么这表明程序设计一开始就不好。此函数可以执行以下两项操作之一:

      • 或者它可以在一些外围硬件上读取一个字节,在这种情况下,函数
        class Handy_Base
        {
         protected:
            static int Handy_protected() { return 0; }
         public:
            static int Handy_public() { return 0; }
        };
        
        template< class Is_Interrupt_Handler >
        class Handy_functions;
        
        // Functions can be used when inside an interrupt handler
        template<>
        struct Handy_functions< In_Interrupt_Handler >
            : Handy_Base
        {
            static int Handy1() { return 1; }
            static int Handy2() { return 2; }
        };
        
        // Functions can be used when inside any function
        template<>
        struct Handy_functions< In_Non_Interrupt_Handler > 
            : Handy_Base
        {
            static int Handy1() { return 4; }
            static int Handy2() { return 8; }
        };
        
        int main()
        {
            using IH_funcs = Handy_functions<In_Interrupt_Handler>;
            std::cout << IH_funcs::Handy1() << '\n';
            std::cout << IH_funcs::Handy2() << '\n';
        
            using Non_IH_funcs = Handy_functions<In_Non_Interrupt_Handler>;
            std::cout << Non_IH_funcs::Handy1() << '\n';
            std::cout << Non_IH_funcs::Handy2() << '\n';
        
        }