4

I have a function which can be simplifed as:

template<typename T>
void my_function(T value_in)
{
   static int my_number{};
   // Do stuff with my_number and value_in
   my_number++;
}

What I want is for my_number to increment on each function call. This would work as I expect if this was not a templated function, however, the behavior of this is not as expected due to multiple functions being generated (at compile time? is generated the proper term?) based on T's type when calling this function elsewhere in my project.

Currently, my solution is to do something like this:

int static_number_incrementer()
{
   static int static_number{};

   return static_number++;
}

template<typename T>
void my_function(T value_in)
{
   int my_number = static_number_incrementer();
   // Do stuff with my_number and value_in
}

This works, but I'm wondering if there is something more "built-in" that I can use in the original implementation of my_function that doesn't require creating a second function?

cam.b
  • 127
  • 9
  • As a tangent, this design seems very susceptible to surprising changes in behavior. I'm curious what problem are you trying to solve by doing this? – Brian61354270 Mar 01 '23 at 23:29
  • 2
    Function local statics are very rarely the right thing. A more traditional way would be to have this function as a class member function and the "saved" value as a member of that class. This would give you much more control over how its initialised and what value its initialised to, and how its accessed and updated. But without more information on what you're trying to achieve its hard to know the right solution. (Additionally function local statics are notoriously hard to unit test as they hide away persistent state) – Mike Vine Mar 01 '23 at 23:33
  • @Brian What would cause surprising changes to the behavior? It seems like an incrementing value wouldn't cause anything crazy, but maybe I'm missing something? Effectively, I'm just trying to track how many times `my_function` gets called, and my_number gets used inside of a string for a kind of "logging". – cam.b Mar 01 '23 at 23:34
  • I suppose that, in your solution, `my_number` doesn't have to be `static` (or `sequence_num` isn't incremented the second time you call `my_function()` with a given `T` type). – max66 Mar 01 '23 at 23:36
  • @MikeVine That makes complete sense, thanks for the answer! I think my helper function is still the "path of least resistance" for my case, but I'm glad to know that way of doing things for the future. Thankfully this is in a unit test "helper function" context already, so I'm not as worried about that side of things causing weird-ness. – cam.b Mar 01 '23 at 23:39
  • @max66 fixed the example, thanks! – cam.b Mar 01 '23 at 23:42

2 Answers2

3

As you already noticed, different template realizations of my_function are different functions. Since the number of calls has to be stored somewhere, this cannot be inside the template function. A very proper way to do it is, as suggested in the comments, to place this information inside a class. At minimum, you could transform your function to a struct with an operator() inside. Something like

// my_function.hpp
struct my_function {
   template <typename T>
   void operator()(T value_in);

private:
   static int static_number;
};

// my_function.cpp
int my_function::static_number = {};

Notice that before c++17 the private static member static_number must be initialized in a separate cpp source file. Within c++17 you can declare the static member static_number as inline, initializing it directly in the class declaration, thus without the need of an additional cpp source code:

// my_function.hpp
// Requires c++17
struct my_function {
   template <typename T>
   void operator()(T value_in);

private:
   inline static int static_number = {};
};

The you call your function with syntax similar to the case of a "normal" function.

my_function{}(x);

Since static_number is static, there is a single static_number for all instancies of my_function. This approach has the benefit that static_number is not accessible from outside.

An alternative solution to yours, is to have a global variable, perhaps "hidden" inside a namescope, so to avoid clashes:

namespace impl {
   int static_number = {};
}

This has however the drawback that static_number is accessible from functions other than my_function.

francesco
  • 7,189
  • 7
  • 22
  • 49
  • _" private static member [...] must be initialized in a separate source file"_ Since _C++17_ you can mark them `inline` and initialize them in the class definition (no out-of-class definition required). – Fareanor Mar 02 '23 at 10:41
  • @Fareanor Thanks a lot for pointing it out, I added this to the answer. – francesco Mar 02 '23 at 12:17
1

I believe you're correct, that multiple versions of the function are getting created, thus giving you multiple static variables.

If your function is within a non-templated class, you can just create a static member of the class. If the function is within a templated class, you could create parent class, and put your static in that (per a similar discussion.

If your function is not within a class then your current solution works.

Curious to see if someone has a better workaround for this.

tanr
  • 99
  • 6
  • Similar to what Mike Vine was saying in his comment. This sums it up nicely thank you. It is not in a class so I'm on the same page as you. – cam.b Mar 01 '23 at 23:43