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

Viewed 848

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 (e.g. 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
(b) ensure a method is called in at most one place

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();
  }
}
4 Answers

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. 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

  • 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.

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).

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. 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).

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).

There is a partial solution to this question using the C preprocessor and GNU inline assembly:

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

Implementation file 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. There is an implementation in another question

Related