Templated Id Generator clearly defined behavior?

I would like not to assign Id all my classes where Id is required, so I wrote a small template generator with the extension.

BaseIdGenerator::makeUniqueId just returns a new Id , every time it is called:

 class BaseIdGenerator { protected: static inline Id makeUniqueId() { static Id nextId = 0; return nextId++; } }; 

To assign an Id to individual classes, the class is simply passed as the IdGenerator template argument:

 template <typename T> Id getId(); template <typename T> class IdGenerator final : public BaseIdGenerator { static Id id; template <typename Type> friend Id getId(); }; template <typename T> inline Id getId() { return IdGenerator<T>::id; } template <typename T> Id IdGenerator<T>::id = IdGenerator<T>::makeUniqueId(); 

This will call makeUniqueId() exactly once per class (even in multi-threaded applications with C ++ 11 due to local static variables protected by the stream)

In action, it looks like this:

 int main() { std::cout << getId<int>() << std::endl; // prints 0 std::cout << getId<bool>() << std::endl; // prints 1 std::cout << getId<char>() << std::endl; // prints 2 } 

This works as expected.

  • Is this correct behavior in C ++?
  • In many cases, using getId() violates this functionality? (Several source files, etc.).
  • Is this a standardized behavior, so the output will be the same on every computer, and will it work as expected in a network application?
+5
source share
2 answers

This will call makeUniqueId () exactly once for each class (even in multi-threaded applications with C ++ 11 due to local static variables protected by the stream)

Initializing local static variables is thread safe. Do not change them , so just having a local static variable in the function will guarantee that it will be created once and only once in multi-threaded programs. Everything you do manually is subject to race conditions and requires synchronization at your end. For example, what you have above is subject to race conditions if you call makeUniqueId() from multiple threads at the same time.

This wikipedia article has a good explanation of how the static local construction of variables works and how they are protected from multi-threaded access. But note that only a static local variable is protected by the language and the compiler.

Is this the correct behavior in C ++?

How do you do it right now, assuming all your code is compiling, yes, this is a well-defined behavior, it does not violate ODR, as it is, if that’s what you are thinking about. However, if you specialize the getId() and / or IdGenerator in the implementation file and link it to another file that does not see this specialization, then you violate ODR, since now there are two definitions of the same thing in the system (one specialized and the other is not). The compiler should not even warn with any diagnostics in this case. Therefore, although it works this way, be careful and learn about ODR. See http://en.cppreference.com/w/cpp/language/definition for more details.

Would much use of getId () violate this functionality? (Several source files, etc.)

You can use getId() as many times as you want. However, the order of static initialization of variables is not specified, so getId() may not return the same values ​​all the time. However, you will have different meanings if you do not have races and ODR violations (as mentioned above)

Is this a standardized behavior, so the output will be the same on every computer, and will it work as expected in a network application?

The initialization order of a static variable is not indicated in translation units, so I would say that this may not be the same on each machine. I really did not know how this would change for a network application. The code to initialize the values ​​is run before the program starts. Thus, all identifiers will be set before any network I / O (provided that you do not have network I / O in the static function called before main() run, in which case one of the values ​​for getId() may not even to be initialized)

+3
source
  • Is this correct behavior in C ++?

Yes, he is well defined.

  • In many cases, using getId() violates this functionality? (Several source files, etc.).

No, not in general. But multi-threaded access can lead to race conditions.

  • Is this a standardized behavior, so the output will be the same on every computer, and will it work as expected in a network application?

Yes, standardization of behavior.

I do not see interference in the network application, except for enthusiasm. Of course, the resulting identifier will be in the same endianess as the host machine where they are generated. To avoid this, use the htonl() function (if Id is a long integer type).

+2
source

Source: https://habr.com/ru/post/1268932/


All Articles