For over years I heard interview questions like: “does C++ have a static constructor” or “what static constructor do”. Of course there is no such type of constructors in C++. However it would be extremely useful…
My proposition is to provide a static constructor and destructor in C++. The static constructor will be executed once before main() function and static destructor just after it. So it works in the same manner as for a global object with one difference: there is no associated object. Static ctor/dtor is called once, even if object was never created. It generally behave as usual static function. Therefore you cannot use this pointer within such ctor/dtor.
I see a numerous purposes for this thing. The most important are:
let’s consider a following example:
class MyPlugin : public Plugin {
private:
Plugin &getPluginInstance() {
static MyPlugin myPlugin;
return myPlugin;
}
public:
static MyPlugin() {
Plugins::registerPlugin(getPluginInstance());
}
static ~MyPlugin() {
Plugins::unregisterPlugin(getPluginInstance());
}
};
Here are the rules:
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/fae9f871-7738-41c3-b058-9c7ae6efd55d%40isocpp.org.--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposal...@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.
For over years I heard interview questions like: “does C++ have a static constructor” or “what static constructor do”. Of course there is no such type of constructors in C++. However it would be extremely useful…
My proposition is to provide a static constructor and destructor in C++. The static constructor will be executed once before main() function and static destructor just after it. So it works in the same manner as for a global object with one difference: there is no associated object. Static ctor/dtor is called once, even if object was never created. It generally behave as usual static function. Therefore you cannot use this pointer within such ctor/dtor.
I see a numerous purposes for this thing. The most important are:
- Easier class registration/deregistration
- New mechanism for singleton implementation
- Class code can be now executed w/o creating object and w/o external call.
- And the best one: Including/Excluding h/cpp file into application allows enabling/disabling functionality without using preprocessor.
let’s consider a following example:
class MyPlugin : public Plugin {
private:
Plugin &getPluginInstance() {
static MyPlugin myPlugin;
return myPlugin;
}
public:
static MyPlugin() {
Plugins::registerPlugin(getPluginInstance());
}
static ~MyPlugin() {
Plugins::unregisterPlugin(getPluginInstance());
}
};
Here are the rules:
- Static constructors of different classes are executed one by one in the same order as corresponding classes were defined.
- Static constructors are always executed before software entry point (the main() function) and always before constructors of all global objects.
- Static destructors are executed one by one in reverse order to the order of corresponding classes definition.
- Static destructors are always executed after software entry point and always after constructors of all global objects.
- If both base and derived class have a static constructor, first is called a static constructor of base class
- If both base and derived class have a static destructor, first is called a static destructor of derived class
- static constructor and destructor have no parameters
- static constructor and destructor behaves as usual static function
- this pointer cannot be used within static constructor nor destructor
Thank you very much for all comments. Please have a look at the simplified code once again:class MyPlugin : public Plugin {
public:
static MyPlugin() {
static MyPlugin myPlugin;Plugins::registerPlugin(myPlugin);
}
~MyPlugin() {
Plugins::unregisterPlugin(*this);
}
};This is a complete, executable code. Previously I have forgotten static keyword for getPluginInstance function. Thank you for pointing this out!Let’s consider the plug-in registration. Currently in C++ to execute class code, you need to at least create an object to execute default constructor’s code. You can also call a static function. However to register a new plug-in, programmer have to write some code.
class MyPlugin : public Plugin {
public:
~MyPlugin() {
Plugins::unregisterPlugin(*this);
}
private:
struct Reg
{
Reg() {
static MyPlugin myPlugin;
Plugins::registerPlugin(myPlugin);
}
};
static inline Reg register;
};With static constructors you can just add a cpp / obj / lib file into your project. Or just include a header file with above class in any source file.
The static constructor of MyPlugin class will create a static object, so you do not need to do this manually in a cpp file. The static constructor also calls Plugins::registerPlugin on the object, so it is also automatically registered. Just by a including header file.After reading your comments I found that there is actually no need for static destructor. As you can create a static object in static constructor, then regular destructor can perform a deregistration. According to this, there is no longer need to consider the execution order.Briefly, the linker should now operate this way:
- Call static constructors form all translation units and libs
- Call constructors for global variables (as usual)
- Call main() function (as usual)
- Call destructors for global variables (as usual)
Please correct me if I am missing something here: If lib file have a static constructor providing plugin registration, it will be automatically executed and there is no longer need to provide a header file associated with the lib to use it in a project.
Please correct me if I am missing something here: If lib file have a static object providing plugin registration, it will be automatically executed and there is no longer need to provide a header file associated with the lib to use it in a project.
So why do we need a static constructor if a static object will do the exact same thing? You're not giving us something new; you're only creating a slightly more convenient way to do it.
So why do we need a static constructor if a static object will do the exact same thing? You're not giving us something new; you're only creating a slightly more convenient way to do it.
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposal...@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/a027b6eb-30ba-428d-a87d-f6d7732db335%40isocpp.org.
So why do we need a static constructor if a static object will do the exact same thing? You're not giving us something new; you're only creating a slightly more convenient way to do it.
Static object with nontrivial constructor in a library you rae linking with:
* might be some dead code you don't need (but might be needed by other code base which uses this static object). In this case constructor call might be freely elliminated by LTO
* or can be a hack in order to have some code which do some lmportant library initializations and must never be eliminated by LTO
Static class constructor is not "more convenient way to have class level or library level initialization in C++". Is completely new feature which provide you the only way to have such initialization. Current approach is attempt to use some side-effect of another feature which is not reliable in real world compilers without disabling important optimizations (there are tricks like get address of a dummy static registrator variable from some function which will be called. Necessity to have such strange code is the best motivation example why static constructors is good feature to add to C++).
So why do we need a static constructor if a static object will do the exact same thing? You're not giving us something new; you're only creating a slightly more convenient way to do it.To have a static object you have to create it first, somewhere outside the class.
On sábado, 23 de julho de 2016 08:39:37 PDT Nicol Bolas wrote:
> Inline variables
> <http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4424.pdf>seem to
> solve this problem. From the PDF, I gather that the intent is that the
> constructor/destructor for inline variables are always called, whether they
> are ODR used or not. And therefore, you can rely on their side-effects.
Has this been implemented as a proof of concept in any compler?
I don't remember this as a consequence of inline variables when we were
discussing them. They were either constexpr or evaluated on use, as opposed to
intialised some time in the past.
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposal...@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/08b9502c-88bb-494d-84c6-69c1ebbf8203%40isocpp.org.
... I fail to see the gains from the additional syntax, at least in C++ (other languages like C# or Java gain from this, but they instantiate objects quite differently). What does it bring that we cannot already do with minimal effort?
... I fail to see the gains from the additional syntax, at least in C++ (other languages like C# or Java gain from this, but they instantiate objects quite differently). What does it bring that we cannot already do with minimal effort?
... I fail to see the gains from the additional syntax, at least in C++ (other languages like C# or Java gain from this, but they instantiate objects quite differently). What does it bring that we cannot already do with minimal effort?Of course you can do it this way, but as I said before, you need to define somewhere in cpp file this: Log::LogFileHandler Log::handler. Therefore it will not work for header-only libraries.
inline variables are going to solve this issue. Using static class constructor initialize static variable is the same type of hack as using dummy unused variable to execute some library initialization code :)
):class MyClass
{
//...
public:
void deps()
{
DEPS(); // somewhere in the early headers you'll have #define DEPS() []{}()
#undef DEPS
#define DEPS() MyClass::deps()
static bool ready = false;
if(!ready)
{
// actual 'static constructor' code
}
}
};
It's possible that some of these I should've found between the lines, in which case I'm sorry. It's also totally ok to state that most of these should remain UB.
Thanks,
-lorro
Hi,
Thank you very much for all invaluable comments!
Updated draft document D0421R1 has been attached.
Please explain how the list is created and advise how
ABIs should deal with this in case of shared libraries and that of static
libraries. Assume you can't modify the linker.
Good hint. I have modified it in the document. Thanks. The static class constructor, is now behaving exactly the same as a default class constructor of global variable, except have no associated object. Therefore the same mechanism can be used to execute it with NULL instead of object pointer. This does not requires to modify linker.
You declare static constructors as if they had a return type of the class type: static MyClass() { ... };, which I think is inconsistent with current C++ grammar. Note that in the position where you've put MyClass, we usually put (loosely speaking) return type. This includes the (non-static) constructor(s), which evaluate to MyClass and have no name. Possible fixes for this include static void() { ... }; and static void MyClass() { ... };, the second being a clash on a typical error that is usually reported separately by the compiler. Or you might use a single name for all types, e.g. static void constructor() { ... };.
I see your point, but you cannot declare function with no name, so if the function returns MyClass it will be: [static] MyClass identifier(…); but in case of default/static class constructor, you have no such problem, because MyClass is always followed by parenthesis and constructors have no returned type. Then we have no ambiguity here. Did I properly understood your comment?
However, if your constructor really returns void, how will you handle errors? Normally constructors are either exception-safe (read: we ignore some exceptions that we want to fail early for) or report error by throwing something. Having a static int constructor_error_code doesn't really help if the constructor doesn't execute fully.
I am not sure about this. Your static int variable if declared in global scope will be initialized before static class constructor is executed. You can then use it to mark failure initially and set it to successful just before returning from constructors code. The exceptions are working as in case of default class constructor for global variables, so will have similar problems here.
Your document requires linker support. That goes way beyond C++: even if we standardized static constructors, if you require linkers to be changed, then it's perhaps the best to first ask linkers to allow for this. This is a dependency for compiler implementers.
I modified the document to avoid linker modification. The static class constructors uses now the same execution mechanism as for constructors of global object.
What about DLL / .so files? It's already kind of a nightmare to have static initialization there. These would need very clear recipes to have them defect free.
It would be very nice to have such functionality finally working in C++. Nevertheless as linker cannot be modified, I am not addressing it anymore in the document. I’ll try to issue another document to cover this problem regarding both static class constructor and global variable initialization.
Correct me if I'm wrong, it'll need at least a static bool per class to know it it's been initialized as two libs might have the same class. In that case, it's just a syntax sugar over static bool constructor_result = []{ ... }()
Could you please explain it a little bit more? When you would like to use this static bool with lambda? I do know it is used to execute code once, but how is this related? The lambda will not be called if object is not created unlike the static class constructor.
Having a static bool (non-const) for each template class I instantiate would make me invest in memory modules.
I do not think this static bool is anywhere needed as it can be determined once on program linking stage.
I suggest underlining (and explaining why is) that, unlike default constructors, they're not auto-generated if missing.
Good hint, I’ll cover it. Thanks. Actually we cannot say they are auto-generated if missing. The static class constructor can initialize static class members through initializer list as non-static members are initialized through class constructor. However, if you have no default constructor, members are initialized with its default constructor. We cannot achieve this static class constructor, just to be backward compatible. If you have no static class constructor, you must define the static member outside the class body as usual. When static class constructor is defined, all static members not defined on initializer list will be initialized with its default constructor, even if they are not defined outside the class body. So the backward compatibility is the reason.
Which brings us to... can't these be unified? As a workaround for some static init issues, we used to have something like (feel free to laugh): […] Terrible as it seems, it actually fixes all the ordering issues re: dependencies, inheritance, multi-init; it can be run from main() or DllMain() by a simple DEPS();, in which case it runs after static initializer. You might take this and modify to your needs when you want to specify the as-if of static ctor implementation. You might want to expose ready (or drop it if you wish) and make tha call to DEPS(); in main()-like functions automatic.
Interesting. Assuming void deps() is also static. In your code you have a chain of execution, but each class depend (indirectly) on the other. Moreover DEPS is in a single translation unit only. Additionally, the more calls you have, the more stack you use. IMHO it is better/safer to build a list (array) of pointers to functions and then execute it one by one. However definitely the best way qould be tu use the static class constructor, which provides more OOP way to do this.
You're okay to allocate one byte per class for static destructors, but didn't seem okay to allocate one bit per class for static constructor? Why is that? Or is there something I'm missing?
Where I stated that? No additional allocations are needed. In the above example with MyPlugin, static object was used to simplify the example. However, of course depending on compiler configuration, empty class can have a zero-length.
do you want static constructors to be (manually) callable?
No, this feature is not available. It was already covered in document.
do you want to be able to take their address?
No, there is no way to do this.
does it make sense to have static constructors with parameters (and if yes, who would fill them: OS passing argc, argv[] or base / descendant, anyone else?)
No, you cannot pass parameters to the static class constructor. It was already covered in document.
is there a way to inherit static ctors? (e.g. AbstractPlugin comes to mind) If yes, how do we get current (most derived) type?
Yes. Thank you for pointing that as I forgotten to cover it in the document. Static class constructors are executed/inherited in the same manner as default constructors.
do you need static construction to be sequential, or just base-before-descendant? allowing for, but not requiring parallel initialization might speed up start-up without putting a burden on compilers.
Sequential approach seems to be the most appropriate and as above mentioned, the static class constructor of base class will be executed first, then are initialized static members defined (and not defined) through the initializer list. Finally the code of static class constructor is executed.
can you instantiate _other_ classes during static construction? it's possible that their static ctor is not yet run, or that you have an inter-dependency.
You can instantiate classes and their static class constructors will be executed before, same as global variables are initialized. The initialization is performed in definition order within single translation unit. Compare its behavior to class constructor execution of global variables.
are vtables constructed by the time constructors are run?
vtables are generated before; as for class constructor of global variables.
can you initialize static (const) reference members and what's the syntax?
Hm, actually why not through initializers list? Any objection?
can you throw an object that's class is being statically constructed?
I am thinking about this use case, but I do not see your point. Can you elaborate a little bit more please?
Best Regards,
Mariusz
main.cpp:57:20: error: constructor cannot be static member function
static Log()--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/4983ce2b-943a-f92e-16f1-4c22ec8fc846%40gmail.com.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CALvx3hYCedQmq1%3D6JiS53JdZ0oTUqCBTaVck3P-tmrRRD0RyGA%40mail.gmail.com.