Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to add constructors/destructors to an unnamed class?

Is there a way to declare a constructor or a destructor in an unnamed class? Consider the following

void f() {     struct {         // some implementation     } inst1, inst2;      // f implementation - usage of instances } 

Follow up question : The instances are ofcourse constructed (and destroyed) as any stack based object. What gets called? Is it a mangled name automatically assigned by the compiler?

like image 325
Nikos Athanasiou Avatar asked Feb 19 '14 22:02

Nikos Athanasiou


People also ask

Can anonymous class have destructor?

Anonymous classes: Cannot have a constructor or destructor.

Can you call constructors and destructors directly?

Yes, it is possible to call special member functions explicitly by the programmer.

Can I inherit the constructor and destructor of a base class?

Constructors and destuctors are not members of the class and not inherited but instead automatically invoked if the sub class has no constructor. up to now this excludes c++ which supports constructor inheritance. It's Me As of now only option 1 is right i.e destructors cannot be inherited.

How do you apply constructors and destructors in a class explain?

Constructors are special class functions which performs initialization of every object. The Compiler calls the Constructor whenever an object is created. Constructors initialize values to object members after storage is allocated to the object. Whereas, Destructor on the other hand is used to destroy the class object.


2 Answers

The simplest solution is to put a named struct instance as a member in the unnamed one, and put all of the functionality into the named instance. This is probably the only way that is compatible with C++98.

#include <iostream> #include <cmath> int main() {    struct {       struct S {          double a;          int b;          S() : a(sqrt(4)), b(42) { std::cout << "constructed" << std::endl; }          ~S() { std::cout << "destructed" << std::endl; }       } s;    } instance1, instance2;    std::cout << "body" << std::endl; } 

Everything that follows requires C++11 value initialization support.

To avoid the nesting, the solution for the construction is easy. You should be using C++11 value initialization for all members. You can initialize them with the result of a lambda call, so you can really execute arbitrarily complex code during the initialization.

#include <iostream> #include <cmath> int main() {    struct {       double a { sqrt(4) };       int b { []{             std::cout << "constructed" << std::endl;             return 42; }()             };    } instance1, instance2; } 

You can of course shove all the "constructor" code to a separate member:

int b { [this]{ constructor(); return 42; }() }; void constructor() {    std::cout << "constructed" << std::endl; } 

This still doesn't read all that cleanly, and conflates the initialization of b with other things. You could move the constructor call to a helper class, at the cost of the empty class still taking up a bit of space within the unnamed struct (usually one byte if it's the last member).

#include <iostream> #include <cmath> struct Construct {    template <typename T> Construct(T* instance) {       instance->constructor();    } };  int main() {    struct {       double a { sqrt(4) };       int b { 42 };       Construct c { this };       void constructor() {          std::cout << "constructed" << std::endl;       }    } instance1, instance2; } 

Since the instance of c will use some room, we might as well get explicit about it, and get rid of the helper. The below smells of a C++11 idiom, but is a bit verbose due to the return statement.

struct {    double a { sqrt(4) };    int b { 42 };    char constructor { [this]{       std::cout << "constructed" << std::endl;       return char(0);   }() }; } 

To get the destructor, you need the helper to store both the pointer to an instance of the wrapped class, and a function pointer to a function that calls the destructor on the instance. Since we only have access to the unnamed struct's type in the helper's constructor, it's there that we have to generate the code that calls the destructor.

#include <iostream> #include <cmath> struct ConstructDestruct {    void * m_instance;    void (*m_destructor)(void*);    template <typename T> ConstructDestruct(T* instance) :       m_instance(instance),       m_destructor(+[](void* obj){ static_cast<T*>(obj)->destructor(); })    {       instance->constructor();    }    ~ConstructDestruct() {       m_destructor(m_instance);    } };  int main() {    struct {       double a { sqrt(4) };       int b { 42 };       ConstructDestruct cd { this };        void constructor() {          std::cout << "constructed" << std::endl;       }       void destructor() {          std::cout << "destructed" << std::endl;       }    } instance1, instance2;    std::cout << "body" << std::endl; } 

Now you're certainly complaining about the redundancy of the data stored in the ConstructDestruct instance. The location where the instance is stored is at a fixed offset from the head of the unnamed struct. You can obtain such offset and wrap it in a type (see here). Thus we can get rid of the instance pointer in the ConstructorDestructor:

#include <iostream> #include <cmath> #include <cstddef>  template <std::ptrdiff_t> struct MInt {};  struct ConstructDestruct {    void (*m_destructor)(ConstructDestruct*);    template <typename T, std::ptrdiff_t offset>    ConstructDestruct(T* instance, MInt<offset>) :       m_destructor(+[](ConstructDestruct* self){          reinterpret_cast<T*>(reinterpret_cast<uintptr_t>(self) - offset)->destructor();       })    {       instance->constructor();    }    ~ConstructDestruct() {       m_destructor(this);    } }; #define offset_to(member)\    (MInt<offsetof(std::remove_reference<decltype(*this)>::type, member)>())  int main() {    struct {       double a { sqrt(4) };       int b { 42 };       ConstructDestruct cd { this, offset_to(cd) };       void constructor() {          std::cout << "constructed " << std::hex << (void*)this << std::endl;       }       void destructor() {          std::cout << "destructed " << std::hex << (void*)this << std::endl;       }    } instance1, instance2;    std::cout << "body" << std::endl; } 

Unfortunately, it doesn't seem possible to get rid of the function pointer from within ConstructDestruct. This isn't that bad, though, since its size needs to be non-zero. Whatever is stored immediately after the unnamed struct is likely to be aligned to a multiple of a function pointer size anyway, so there may be no overhead from the sizeof(ConstructDestruct) being larger than 1.

like image 98
Kuba hasn't forgotten Monica Avatar answered Oct 08 '22 00:10

Kuba hasn't forgotten Monica


You can not declare a constructor or destructor for an unnamed class because the constructor and destructor names need to match the class name. In your example, the unnamed class is local. It has no linkage so neither mangled name is created.

like image 42
Vlad from Moscow Avatar answered Oct 07 '22 22:10

Vlad from Moscow