14

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();
  }
}
yoyoy
  • 375
  • 3
  • 13
  • 1
    This can be accomplished at runtime using `__FILE__` and `__LINE__` macros. I don't it's possible to get a compile time error for this but I may be wrong. With that said, this seems like an odd requirement. The most similar thing I've done is ensuring that a single instance of a class exists at one time. – Indiana Kernick Mar 27 '20 at 03:43
  • 2
    Don't make it a method. Put the code inline in that one place. – user207421 Mar 27 '20 at 03:43
  • 2
    I think you could also do this with a lambda (it could be an empty lambda) because the type of a closure is unique for each lambda. Again, this would be a runtime error but that's not what you asked for. If you provide more details about the problem that you're trying to solve then we might be able to find a way around this. – Indiana Kernick Mar 27 '20 at 03:47
  • 2
    You could use the non-standard `__COUNTER__` macro to do this. Something like `static_assert(__COUNTER__ == 0); my_class.my_method();`. However, the counter resets in each translation unit so you could only check that the function is called once per translation unit. – Indiana Kernick Mar 27 '20 at 04:07
  • 1
    The only way that I can think of that would meet all of your requirements is to write a script that scans all of your source files for the function call. This seems kind of silly though. – Indiana Kernick Mar 27 '20 at 04:15
  • 4
    Why do you want to do that? Part of the point of a function is that it can be called from multiple places. –  Mar 27 '20 at 04:25
  • 4
    You should explain *why* you want to do this. Maybe the solution you're asking for isn't the best to accomplish your real goals. – tenfour Mar 30 '20 at 09:02
  • 2
    What if you have `MyClass o1, o2; o1.my_method(); o2.my_method();`. Should this compile or not? – geza Mar 31 '20 at 10:18
  • Apart from the fun of trying to solve a contrived problem this seems absolutely bonkers. Please say that you're not trying to solve a _real_ problem this way. – Ted Lyngmo Apr 02 '20 at 23:34
  • 1
    Yes, this is mostly a curiosity. I think there are maybe real use cases for ensuring a method is called *at least once*, which lead me to wonder about at most once. I don't think I would actually do this in practice, but it's interesting to learn about the language. – yoyoy Apr 03 '20 at 00:57
  • The definition of "called at one place" seem to be flawed here: allowing function to be invoked multiple times in the loop scope is the same as allowing it to be called multiple times in different scopes. – user7860670 Apr 05 '20 at 10:35
  • What exactly do you mean by calling? If it is called once, in a function that is inlined at multiple places, how does that count? And what if its is called in a function template that is instantiated (and called) multiple times? And what if its address is taken, but never used? Or it address is taken, and it *might* (depending on run-time conditions) be called *via the pointer* at mu;ltiple places? – Wouter van Ooijen Apr 17 '20 at 14:56

4 Answers4

5

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.

Rumburak
  • 3,416
  • 16
  • 27
1

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

Adi Shavit
  • 16,743
  • 5
  • 67
  • 137
1

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.

atomsymbol
  • 370
  • 8
  • 11
0

Use a constexpr counter. There is an implementation in another question

SD57
  • 73
  • 7
  • 1
    It sounds like this method is ill-formed. –  Mar 31 '20 at 14:24
  • The issue http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#2118 referenced in a reply to that question states that it is a bug of the standard and should be made ill-formed. – SD57 Mar 31 '20 at 14:32
  • So it's not ill-formed, at least not yet? –  Mar 31 '20 at 14:34
  • If it's not yet ill-formed it should be used by as much people as possible as quickly as possible, so that they would have to support this use case! – user1685095 Mar 31 '20 at 22:18