简体   繁体   English

确保在编译时只在一个地方调用方法

[英]Ensure at compile time that a method is called in exactly one place

I am curious about whether it is possible to ensure at compile time that a method is called in exactly one place.我很好奇是否有可能在编译时确保在一个地方调用一个方法。

Note that it is OK if the function is called more than once (eg in a loop) - but it should not be called in two separate loops.请注意,如果函数被调用多次(例如在一个循环中)是可以的 - 但它不应该在两个单独的循环中调用。

This can be broken into two parts, I am also interested in solutions that cover either part:这可以分为两部分,我也对涵盖任一部分的解决方案感兴趣:
(a) ensure a method is called in at least one place (a) 确保至少在一个地方调用一个方法
(b) ensure a method is called in at most one place (b) 确保一个方法最多在一个地方被调用

I have full control over the structure of the code, and different idioms that achieve the same idea are welcome.我可以完全控制代码的结构,欢迎使用实现相同想法的不同习语。

// class.h

class MyClass {
  public:
    void my_method();
}

The following should not compile (never called)以下不应编译(从未调用)

#include "class.h"

int main() {
  MyClass my_class;
}

The following should not compile (called in more than one place)以下不应编译(在多个地方调用)

#include "class.h"

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

The following should compile (called in exactly one place):以下应该编译(在一个地方调用):

#include "class.h"

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

Low Tech Approach:低技术方法:

Since you have control over the code structure (which includes the build system, I assume), here is a low tech solution:由于您可以控制代码结构(我假设其中包括构建系统),因此这是一个低技术解决方案:

  • make the function name sufficiently unique使函数名称足够独特
  • grep for the function name in your code. grep 用于代码中的函数名称。 You are expecting it twice (assuming that you declaration and definition are colocated):您期待它两次(假设您的声明和定义位于同一位置):
    • Once in the header一旦在标题中
    • Once at the single call site一旦在单个呼叫站点

Alternatively:或者:

If you really, really, really want to solve it with C++, then you could try如果你真的,真的,真的想用 C++ 解决它,那么你可以试试

  • Use a compile time counter to figure out the number of uses within a compilation units使用编译时间计数器计算编译单元内的使用次数
  • Make sure that the function would violate ODR if the header is included in multiple compilation units.如果头文件包含在多个编译单元中,请确保该函数会违反 ODR。

However, compile time counters are black magic (says I, and I really like TMP), and forcing ODR violations for this purpose seems like similar voodoo (at least you would require a test case that fails to link).然而,编译时间计数器是黑魔法(我说,我真的很喜欢 TMP),为此目的强制 ODR 违规似乎是类似的伏都教(至少你需要一个无法链接的测试用例)。

But seriously:不过实话说:

Don't do this.不要这样做。 Whatever you do, it can be perverted with almost no effort by a wrapper function:无论你做什么,它都可以被包装函数毫不费力地扭曲:

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

MyClass::my_method() is called only in the wrapper. MyClass::my_method()仅在包装器中调用。 Everybody else just calls the wrapper which is probably even inlined by the compiler.其他人只是调用甚至可能由编译器内联的包装器。

As others suggested: It might be much more helpful if you would explain what you are trying to do.正如其他人所建议的那样:如果您能解释一下您要做什么,可能会更有帮助。

Here's a rough idea that may work (too long for a comment - but incomplete for a good SO answer).这是一个可能有用的粗略想法(评论太长了 - 但对于一个好的 SO 答案来说不完整)。

You may be able to achieve this by counting/checking template instantiations.您可以通过计算/检查模板实例来实现这一点。
Templates are instantiated only upon use .模板仅在使用时实例化。

Similarly, template method/function bodies are not parsed nor compiled or linked (beyond ensuring valid syntax) if they are never called.类似地,如果模板方法/函数体从未被调用,则它们不会被解析、编译或链接(除了确保有效的语法)。 This means that any instantiations within their bodies are not made).这意味着不会在它们的主体内进行任何实例化)。

You may be able to create a template that maintains some global instantiation count and static assert on that (or some other TMP mechanism to check past instantiations).您可以创建一个模板来维护一些全局实例化计数和静态断言(或其他一些 TMP 机制来检查过去的实例化)。

There is a partial solution to this question using the C preprocessor and GNU inline assembly:这个问题有一个使用 C 预处理器和 GNU 内联汇编的部分解决方案:

Header file ah :头文件ah

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:");

Implementation file a.cc :实现文件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;
}

This solution is partial in the sense that it does not prevent the program to call the method beginning with the underscore directly without using the wrapper macro.这个解决方案是部分的,因为它不会阻止程序在不使用包装宏的情况下直接调用以下划线开头的方法。

Use a constexpr counter.使用 constexpr 计数器。 There is an implementation in another question在另一个问题中有一个实现

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM