0

I have a C++ library that works on some numeric values, this values are not available at compile time but are immediatly available at runtime and are based on machine-related details, in short I need values like display resolution, the number of CPU cores and so on.

The key points of my question are:

  • I can't ask to the user to input this values ( both the coders/user of my lib and the final user )
  • I need to do this warm up only once the application starts, it's a 1 time only thing
  • this values are later used by methods and classes

the possible solutions are:

  • build a data structure Data, declare some Data dummy where dummy is the name of the variable used to store everything and the contructor/s will handle the one time inizialization for the related values
  • wrap something like the first solution in a method like WarmUp() and putting this method right after the start for the main() ( it's also a simple thing to remember and use )

the big problems that are still unsolved are:

  • the user can declare more than 1 data structure since Data it's a type and there are no restrictions about throwing 2-4-5-17 variables of the same type in C++
  • the WarmUp() method can be a little intrusive in the design of the other classes, it can also happen that the WarmUp() method is used in local methods and not in the main().

I basically need to force the creation of 1 single instance of 1 specific type at runtime when I have no power over the actual use of my library, or at least I need to design this in a way that the user will immediately understand what kind of error is going on keeping the use of the library intuitive and simple as much as possible.

Can you see a solution to this ?


EDIT:

my problems are also more difficult due to the fact that I'm also trying to get a multi-threading compatible data structure.

user2244984
  • 449
  • 4
  • 11
  • You could have it as some kind of context object the user need to instantiate and provide when calling your lib? Or make a singleton with a private constructor and a static getter; cry a little; then go on with your life :) – Skurmedel Apr 05 '13 at 07:47
  • what you exactly mean with "context object" ? Can you provide C++ code for this ? – user2244984 Apr 05 '13 at 07:48
  • 4
    I think you're over-designing a simple solution. Just create a static, exported variable in your library namespace and tell the users to use it... or to not use it. Any hoopaloops you add to simple usage will be accompanied with equal counter-reactive force, such as, hacks and workarounds (this is C++, not Java, C# or Javascript). This is then a problem of library initialization order, which is system-specific. – ActiveTrayPrntrTagDataStrDrvr Apr 05 '13 at 07:50
  • Honestly, your question is not very clear. Every answer understood something very different! – Basile Starynkevitch Apr 05 '13 at 07:51
  • @BasileStarynkevitch I need to store, in an unique way, values that are available at runtime: what is not clear ? – user2244984 Apr 05 '13 at 08:01
  • @ActiveTrayPrntrTagDataStrDrvr at this point it's better to focus on a `Data` type and design it to support a good multithreaded environment and tell to the user to just create 1 single instance. – user2244984 Apr 05 '13 at 08:03
  • I don't see how you came from "I need this initialized before the library is used" to "This must be done exactly one time". What would happen if the users of the library call the initialization routine twice? It may even be necessary to support multiple initialization calls - what if the client of the library has two modules that use the same library and occasionally needs to use them both in the same project? – Alex Apr 05 '13 at 08:03
  • @Alex He's presenting a classical scenario where a "singleton" is the best solution. That doesn't mean that it's the only solution, since as you say, it probably won't break any code if there is a second instance. (In C++, the usual motivation for using a singleton is to control order of initialization. The fact that there can never be more than one instance is secondary.) – James Kanze Apr 05 '13 at 08:11

3 Answers3

2

What about to use lazily-create singleton? E.g

struct Data
{
  static Data & instance()
  {
    static Data data_;
    return data_;
  }
private:
  Data()
  {
    //init
  }
}

Data would be initialized on first use, when you call Data::instance()

Edit: as for multithreading, read efficient thread-safe singleton in C++

Edit2 realisation using boost::call_once

Community
  • 1
  • 1
kassak
  • 3,974
  • 1
  • 25
  • 36
  • so `Data::instance()` would be the equivalent of my `WarmUp()` ? Any name on actual libraries using this pattern ? – user2244984 Apr 05 '13 at 08:04
  • @user2244984 Not exactly. You do not need explicitly initialize `Data`. This method return only onstance of `Data`. If it is unitialized, it initializes it. So it will be initialized on first use – kassak Apr 05 '13 at 08:07
  • 1
    The answer in the link isn't really correct. The conclusion of the cited paper is that double-checked locking cannot be done correctly without some very system specific code---inline assembler, generally. More to the point, it's normally sufficient for thread safety to ensure that the singleton is initialized before starting any threads, either in static initialization or as the first thing in main. – James Kanze Apr 05 '13 at 08:07
  • 1
    @JamesKanze or using `boost::call_once` =). Or `std::call_once` for C++11 http://en.cppreference.com/w/cpp/thread/call_once – kassak Apr 05 '13 at 08:22
  • @kassak Why be simple when you can be complicated. Something like: `Singleton* Singleton::myInstance = &Singleton::instance();` does the trick quite well, and avoids all of the overhead of `std::call_once`. (Also, you normally do _not_ want a `scoped_ptr` here, because you normally do _not_ want the object to be deleted. Deleting it just opens the door to order of destruction problems.) – James Kanze Apr 05 '13 at 08:47
  • @JamesKanze I do not understand, how your piece of code would work in multithreaded environment without `call_once`? Or you mean explicit initialization at static initialization time? Or at the begining of main? – kassak Apr 05 '13 at 09:01
  • @kassak `Singleton::myInstance` has static lifetime. It is initialized before entering `main` (or before any other code in the DLL), so before threading has started. The compiler ensures that it is executed exactly once, so you don't need `call_once`. (I'm not sure you ever need `call_once` since the standard requires the constructor of `std::mutex` to be a `constexpr`. I think the main use of `call_once` was to ensure the construction of a `mutex`.) – James Kanze Apr 05 '13 at 10:38
0

Warmup is usually related to performance issues (and makes me think of the processor cache, see the __builtin_prefetch of GCC).

Maybe you want to make a singleton class, there are many solutions to this (e.g. this C++ singleton tutorial).

Also, if performance is the primary concern, you could consider that the configured parameters are given at initialization (or even at installation) time. Then, you could specialize your code, perhaps as simply as having templates and instanciating them at first by emitting (at runtime = initialization time) the appropriate C++ stub source code and compiling it (at "runtime", e.g. at initialization) and dynamically loading it (using plugins & dlopen ...). See also this answer.

Community
  • 1
  • 1
Basile Starynkevitch
  • 223,805
  • 18
  • 296
  • 547
0

First, as others have said, the obvious (and best) answer to this is a singleton. Since you've added the multithreading requirement, however: there are two solutions, depending on whether the object will be modified by code using the singleton. (From your description, I gather not.) If not, then it is sufficient to use the "naïve" implementation of a singleton, and ensure that the singleton is initialized before threads are started. If no thread is started before you enter main (and I would consider it bad practice otherwise), then something like the following is largely sufficient:

class Singleton
{
    static Singleton const* ourInstance;
    Singleton();

    Singleton( Singleton const& );
    Singleton& operator=( Singleton const& );

public:
    static Singleton const& instance();
};

and in the implementation:

Singleton const* Singleton::ourInstance = &Singleton::instance();

Singleton const&
Singleton::instance()
{
    if ( ourInstance == NULL ) {
        ourInstance = new Singleton;
    }
    return *ourInstance;
}

No locking is necessary, since no thread will be modifying anything once threading starts.

If the singleton is mutable, then you have to protect all access to it. You could do something like the above (without the const, obviously), and leave the locking to the client, but in such cases, I'd prefer locking in the instance function, and returning an std::shared_ptr with a deleter which frees the lock, which was acquired in the instance function. I think something like the following could work (but I've never actually needed it, and so haven't tried it):

class Singleton
{
    static Singleton* ourInstance;
    static std::mutex ourMutex;

    class LockForPointer
    {
    public:
        operator()( Singleton* )
        {
            Singleton::ourMutex.unlock();
        }
    };
    class LockForInstance
    {
        bool myOwnershipIsTransfered;
    public:
        LockForInstance
            : myOwnershipIsTransfered( false )
        {
            Singleton::ourMutex.lock();
        }
        ~LockForInstance()
        {
            if ( !myOwnershipIsTransfered ) {
                Singleton::ourMutex.unlock();
            }
        }
        LockForPointer transferOwnership()
        {
            myOwnershipIsTransfered = true;
            return LockForPointer();
        }
    };
public:
    static std::shared_ptr<Singleton> instance();
};

and the implementation:

static Singleton* ourInstance = NULL;
static std::mutex ourMutex;

std::shared_ptr<Singleton>
Singleton::instance()
{
    LockForInstance lock;
    if ( ourInstance == NULL ) {
        ourInstance = new Singleton;
    }
    return std::shared_ptr<Singleton>( ourInstance, lock.transferOwnership() );
}

This way, the same lock is used for the check for null and for accessing the data.

James Kanze
  • 150,581
  • 18
  • 184
  • 329