确保在编译时在一个位置恰好调用了一个方法


15

我很好奇能否在编译时确保在一个地方恰好调用一种方法。

请注意,如果多次(例如在循环中)调用函数是可以的-但不应在两个单独的循环中调用该函数。

这可以分为两部分,我也对涵盖这两个部分的解决方案感兴趣:
(a)确保在至少一个地方
调用方法(b)确保在一个地方最多调用方法

我对代码的结构拥有完全的控制权,欢迎实现相同想法的不同习惯用法。

// class.h

class MyClass {
  public:
    void my_method();
}

以下内容不应编译(从不调用)

#include "class.h"

int main() {
  MyClass my_class;
}

以下内容不应编译(在多个位置调用)

#include "class.h"

int main() {
  MyClass my_class;
  my_class.my_method();
  while(true) {
    my_class.my_method();
  }
}

以下应编译(恰好在一个地方调用):

#include "class.h"

int main() {
  MyClass my_class;
  while(true) {
    my_class.my_method();
  }
}

2
不要把它当作一种方法。将代码内联在该位置。
user207421

2
我认为您也可以使用lambda(它可以是空lambda)来执行此操作,因为每个lambda的闭包类型都是唯一的。同样,这将是运行时错误,但这不是您要的。如果您提供有关您要解决的问题的更多详细信息,那么我们也许可以找到解决此问题的方法。
印第安纳州克尼克

2
您可以使用非标准__COUNTER__宏来执行此操作。有点像static_assert(__COUNTER__ == 0); my_class.my_method();。但是,计数器会在每个翻译单元中重置,因此您只能检查每个翻译单元是否调用了该功能。
印第安纳·克尼克

4
你为什么要这么做?函数的部分要点是可以从多个地方调用它。
Chipster

4
您应该解释为什么要这样做。也许您所要求的解决方案并不是实现您真正目标的最佳方法。
tenfour

Answers:


6

技术含量低的方法:

由于您可以控制代码结构(我认为其中包括构建系统),因此这是一个技术含量较低的解决方案:

  • 使函数名称足够唯一
  • grep表示代码中的函数名称。您期望两次(假设您的声明和定义位于同一位置):
    • 进入标题
    • 一次到达单个呼叫站点

或者:

如果您真的要用C ++解决它,那么您可以尝试

  • 使用编译时间计数器来计算编译单元内的使用次数
  • 如果标头包含在多个编译单元中,请确保该函数将违反ODR。

但是,编译时间计数器是不可思议的(我说过,我真的很喜欢TMP),为此目的而强迫违反ODR的行为就像是伏都教(至少需要一个无法链接的测试用例)。

不过实话说:

不要这样 无论您做什么,包装函数都可以毫不费力地将其变形:

auto call_my_method(MyClass& o)
{
   return o.my_method();
}

MyClass::my_method()仅在包装器中调用。其他人都只是调用包装器,该包装器甚至可能被编译器内联。

正如其他人所建议的那样:如果您要解释自己想做的事情,可能会更有帮助。


1

这是一个可行的粗略想法(对于评论来说太长了-但是对于好的SO回答来说还不完整)。

您可能可以通过计算/检查模板实例来实现。
模板仅在使用时实例化。

同样,如果永不调用模板方法/函数主体,则不会对它们进行解析,编译或链接(除了确保有效的语法外)。这意味着不会在其体内进行任何实例化)。

您也许可以创建一个模板,以维护一些全局实例化计数和对此进行静态声明(或其他一些TMP机制来检查过去的实例化)。


“全局”实例化计数对于当前编译单元而言是本地的。
atomsymbol

1

使用C预处理程序和GNU内联汇编可以部分解决此问题:

头文件a.h

struct A {
    // Do not call this method directly, use the macro below to call it
    int _method_vUcaJB5NKSD3upQ(int i, int j);
};

// Use inline assembly to ensure that this macro is used at most once
#define method_vUcaJB5NKSD3upQ(args...) \
    _method_vUcaJB5NKSD3upQ(args); \
    asm (".global once_vUcaJB5NKSD3upQ; once_vUcaJB5NKSD3upQ:");

实施文件a.cc

#include <iostream>
#include "a.h"

int A::_method_vUcaJB5NKSD3upQ(int i, int j) { return i+j+5; }

// Ensure that the macro is used at least once
extern "C" const char once_vUcaJB5NKSD3upQ;
static const char get_vUcaJB5NKSD3upQ = once_vUcaJB5NKSD3upQ;

int main() {
    A a;
    for(int i=0; i<7; i++) {
        // Use a separate statement to call the method
        // (terminated by a semicolon, it cannot be a sub-expression)
        auto x = a.method_vUcaJB5NKSD3upQ(2, 3);
        std::cout << x << std::endl;
    }
    return 0;
}

这种解决方案是局部的,因为它不会阻止程序不使用包装宏直接从下划线开始调用该方法。


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.