8

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.

greydet
  • 5,509
  • 3
  • 31
  • 51
  • 6
    Why not wrap the calls to the C API into a class the does the initialization through RAII and use that? You can then require that class as an argument to your constructor. – pmr Nov 21 '12 at 15:03
  • Or use a singleton and initialize on-demand? – Kos Nov 21 '12 at 15:07
  • [The C++ FAQ](http://www.parashift.com/c++-faq-lite/static-init-order-on-first-use.html) ftw. – Lundin Nov 21 '12 at 15:21
  • 2
    Y'know... not even the standard library implementors, who seem to be quite competent C++ coders, could prevent all instances of problematic code use. They handle this problem by mentioning in the documentation that doing certain things results in undefined behaviour, and relying on devs to read those docs and not do those stupid things. This isn't a bad solution. – Rook Nov 21 '12 at 15:47
  • @Rook Yes I think documentation is the only solution for this. I was just wondering if a technical (& not too complex) solution exists for this – greydet Nov 21 '12 at 15:49
  • Yeah. It's not up to you to write code that prevents people from using it poorly. It's up to you to tell them how to use it *correctly*, no more. What you're trying to do is extremely futile and counter-productive. Spend this time worrying about things that matter rather than trying to impose your ideals on other people who will be using your code. – user229044 Nov 21 '12 at 15:50
  • @Rook you can combine a singleton pattern with placement new – Mauro H. Leggieri Nov 21 '12 at 16:39

6 Answers6

7

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.

Community
  • 1
  • 1
pmr
  • 58,701
  • 10
  • 113
  • 156
  • @SergeyK. Thanks. I added a bunch of links to further material. – pmr Nov 21 '12 at 15:25
  • This solution could make it only if the C APIs and the final application is under my control. Obviously this is not the case, my class is part of a library which depends on other external libraries. 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. But I suspect this is simply not possible given my constraints. – greydet Nov 21 '12 at 15:39
  • @greydet Why wouldn't it work? You depend on the library, so just add a wrapper for it in your own library. Clients will not be able to create objects of your type without creating an object of that wrapper. Which is essentially what you want: Restrict how to use your object. – pmr Nov 21 '12 at 15:50
  • No, wrapping C calls in classes is not what RAII describes. Please search for the term on SO or google. – Sebastian Mach Nov 21 '12 at 16:18
  • @phresnel This is not completely about the C API, but the fact that it requires initialization. You can treat the API as a resource and apply RAII to it. – pmr Nov 21 '12 at 16:19
  • @pmr: Sure and that is good. But your last paragraph's wording is misleading. – Sebastian Mach Nov 22 '12 at 11:07
1

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! :-)

Grimm The Opiner
  • 1,778
  • 11
  • 29
  • 1
    Please, read the question. This neither prevents allocation on the stack nor as global object. – Sergey K. Nov 21 '12 at 15:13
  • Also, in C++11 you would rather write `void* operator new(size_t size) = delete;` – Lstor Nov 21 '12 at 15:15
  • 1
    "Is there a way to force a class to **be instantiated on the stack** or **at least** [suggesting this is an acceptable rather than *preferred* solution] prevent it to be global in C++?" – Grimm The Opiner Nov 21 '12 at 15:18
  • @user1158692 This way you only prevent heap allocation, you do not prevent global instantiation. – greydet Nov 21 '12 at 15:32
  • 1
    @greydet Originally he worded the question so that was a possible, less desirable, *option*. The question has changed a lot! :) – Grimm The Opiner Nov 21 '12 at 15:51
  • @user1158692 No the question did not change, the only changes are the two edits to add more details on the constraints to respect. But the question always asked for a way to "at least prevent global instatiation" and preferable for it to be on stack. Your answer only give a solution to prevent heap allocation which does not prevent global instantiation. – greydet Nov 21 '12 at 16:01
1

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.

Matthieu M.
  • 287,565
  • 48
  • 449
  • 722
0

You can try using the Singleton pattern

Mauro H. Leggieri
  • 1,084
  • 11
  • 25
0

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

Not really. You could make constructor private and create said object only using factory method, but nothing would really prevent you from using said method to create global variable.

If global variables were initialized before application enters "main", then you could throw exception from the constructor before "main" sets some flag. However, it is up to implementation to decide when initialize global variables. So, they could be initialized after application enters "main". I.e. that would be relying on undefined behavior, which isn't a good idea.

You could, in theory, attempt to walk the call stack and see from there it is called. However, compiler could inline constructor or several functions, and this will be non-portable, and walking call stack in C++ will be painful.

You could also manually check "this" pointer and attempt to guess where it is located. However, this will be non-portable hack specific to this particular compiler, OS and architecture.

So there is no good solution I can think of.

As a result, the best idea would be to change your program behavior, as others have already suggeste - make a singleton class that initializes your C api in constructor, deinitializes it in destructor, and request this class when necessary via factory method. This will be the most elegant solution to your problem.

Alternatively, you could attempt to document program behavior.

SigTerm
  • 26,089
  • 6
  • 66
  • 115
-2

To allocate a class on the stack, you simply say

FooClass foo; // NOTE no parenthesis because it'd be parsed 
              // as a function declaration. It's a famous gotcha.

To allocate in on the heap, you say

std::unique_ptr<FooClass> foo(new FooClass()); //or
FooClass* foop = new FooClass(); // less safe

Your object will only be global if you declare it at program scope.

Ari
  • 1,102
  • 9
  • 17
  • 1
    Even better, in C++11: `std::shared_ptr foop = std::make_shared();` But that's not what OP is asking at all. – Lstor Nov 21 '12 at 15:17
  • As Lstor said I know the different ways to instiate C++ objects. I am looking for a way to make it impossible to instanciate my class globally. – greydet Nov 21 '12 at 15:27
  • Ah, I totally misunderstood the question then. – Ari Nov 21 '12 at 15:31