Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ - Prevent global instantiation?

Is there a way to force a class to be instantiated on the stack or at least prevent it to be global in C++?

I want to prevent global instantiation because the constructor calls C APIs that need previous initialization. AFAIK there is no way to control the construction order of global objects.

Edit: The application targets an embedded device for which dynamic memory allocation is also prohibited. The only possible solution for the user to instanciate the class is either on the stack or through a placement new operator.

Edit2: My class is part of a library which depends on other external libraries (from which come the C APIs). I can't modify those libraries and I can't control the way libraries are initialized in the final application, that's why I am looking for a way to restrict how the class could be used.

like image 394
greydet Avatar asked Nov 21 '12 15:11

greydet


3 Answers

Instead of placing somewhat arbitrary restrictions on objects of your class I'd rather make the calls to the C API safe by wrapping them into a class. The constructor of that class would do the initialization and the destructor would release acquired resources.

Then you can require this class as an argument to your class and initialization is always going to work out.

The technique used for the wrapper is called RAII and you can read more about it in this SO question and this wiki page. It originally was meant to combine encapsulate resource initialization and release into objects, but can also be used for a variety of other things.

like image 121
pmr Avatar answered Nov 17 '22 21:11

pmr


Half an answer: To prevent heap allocation (so only allow stack allocation) override operator new and make it private.

void* operator new( size_t size );

EDIT: Others have said just document the limitations, and I kind of agree, nevertheless and just for the hell of it: No Heap allocation, no global allocation, APIs initialised (not quite in the constructor but I would argue still good enough):

class Boogy
{
public:

    static Boogy* GetBoogy()
    {
        // here, we intilialise the APIs before calling
        // DoAPIStuffThatRequiresInitialisationFirst()
        InitAPIs();
        Boogy* ptr = new Boogy();
        ptr->DoAPIStuffThatRequiresInitialisationFirst();
        return ptr;
    }

    // a public operator delete, so people can "delete" what we give them
    void operator delete( void* ptr )
    {
        // this function needs to manage marking array objects as allocated                        
        // or not
    }

private:

    // operator new returns STACK allocated objects.  
    void* operator new( size_t size )
    {
        Boogy* ptr = &(m_Memory[0]);
        // (this function also needs to manage marking objects as allocated 
        // or not)
        return ptr;
    }

    void DoAPIStuffThatRequiresInitialisationFirst()
    {
        // move the stuff that requires initiaisation first
        // from the ctor into HERE.
    }

    // Declare ALL ctors private so no uncontrolled allocation, 
    // on stack or HEAP, GLOBAL or otherwise, 
    Boogy(){}

    // All Boogys are on the STACK.
    static Boogy m_Memory[10];

};

I don't know if I'm proud or ashamed! :-)

like image 34
Grimm The Opiner Avatar answered Nov 17 '22 22:11

Grimm The Opiner


You cannot, per se, prevent putting objects as globals. And I would argue you should not try: after all, why cannot build an object that initialize those libraries, instantiate it globally, and then instantiate your object globally ?

So, let me rephrase the question to drill down to its core:

How can I prevent my object from being constructed before some initialization work has been done ?

The response, in general, is: depends.

It all boils down at what the initialization work is, specifically:

  • is there a way to detect it has not been called yet ?
  • are there drawbacks to calling the initialization functions several times ?

For example, I can create the following initializer:

class Initializer {
public:
    Initializer() { static bool _ = Init(); (void)_; }

protected:
    // boilerplate to prevent slicing
    Initializer(Initializer&&) = default;
    Initializer(Initializer const&) = default;
    Initializer& operator=(Initializer) = default;

private:
    static bool Init();
}; // class Initializer

The first time this class is instantiated, it calls Init, and afterwards this is ignored (at the cost of a trivial comparison). Now, it's trivial to inherit (privately) from this class to ensure that by the time your constructor's initializer list or body is called the initialization required has been performed.

How should Init be implemented ?

Depends on what's possible and cheaper, either detecting the initialization is done or calling the initialization regardless.

And if the C API is so crappy you cannot actually do either ?

You're toast. Welcome documentation.

like image 1
Matthieu M. Avatar answered Nov 17 '22 23:11

Matthieu M.