Can I create side effects to instantiate?

So, let's start with a code that does what I want, but I would like to teach it a new trick (if possible).

#include <string>
#include <iostream>

class A {}; class B {}; class C {}; class D {};

template<typename T> struct traits { };
template<> struct traits<A> { static std::string code() {return "class A";}};
template<> struct traits<B> { static std::string code() {return "class B";}};
template<> struct traits<C> { static std::string code() {return "class C";}};
template<> struct traits<D> { static std::string code() {return "class D";}};

std::string RegisterClass(const std::string & c){
    std::cout << "Registering " << c << '\n';
    return c;
}

template<typename T> void printMe() {
    static std::string t = RegisterClass(traits<T>::code());
    std::cout << "Printing " << traits<T>::code() << '\n';
}

int main(void)
{
    printMe<B>(); 
    printMe<B>(); 
    printMe<C>(); 
    return 0;
}

The way out, as you might expect, is that “registration” occurs only once and only for the types with which the template was created:

Registering class B
Printing class B   
Printing class B   
Registering class C
Printing class C

I would like to have only those classes that were used to "register" myself before the first function call printMe. Thus, the result will look like this:

Registering class B
Registering class C
Printing class B   
Printing class B   
Printing class C

Superficially, this seems possible. The compiler “knows” what types were used to create the instance. If I could store this information in some kind of global or static thing, I would just process it at the beginning main().

. , . , " ? - , " "?

: , . A,B,C and D - B and C, , - .

+4
5

, -, . :

template<typename T>
struct RegisterClass
{
    static RegisterClass instance;
    RegisterClass()
    {
        std::cout << "Registering " << traits<T>::code() << '\n';
    }
    static RegisterClass& doRegister() { return instance; }
};
template<typename T>
RegisterClass<T> RegisterClass<T>::instance;

template<typename T> void printMe() {
    RegisterClass<T>::doRegister();
    std::cout << "Printing " << traits<T>::code() << '\n';
}

.

- doRegister , .

. garantees cin, cout,...? , .

+2

, , . .

#include <string>
#include <iostream>

class A {}; class B {}; class C {}; class D {};

template<typename T> struct traits { };
template<> struct traits<A> { static std::string code() {return "class A";}};
template<> struct traits<B> { static std::string code() {return "class B";}};
template<> struct traits<C> { static std::string code() {return "class C";}};
template<> struct traits<D> { static std::string code() {return "class D";}};

template<typename T> 
void registerClass(){
    std::cout << "Registering " << traits<T>::code() << '\n';
}

template<typename T> void printMe() {
    std::cout << "Printing " << traits<T>::code() << '\n';
}

struct Init
{
   Init();
};

Init::Init()
{
   registerClass<A>();
   registerClass<B>();
   registerClass<C>();
   registerClass<D>();
}

static Init init;

int main(void)
{
    printMe<B>(); 
    printMe<B>(); 
    printMe<C>(); 
    return 0;
}
+1

- .

namespace {
    template<typename T>
    class registrar {
    public:
        registrar() {
            std::string t = RegisterClass(traits<T>::code());
        }
    };
}
template <class type> struct traits {
    virtual std::string code() = 0; // override this
private:
    static registrar m_registrar;
};
registrar traits<A>::m_registrar;

, T traits<T> registrar<T>::registrar() .

+1

, , , .

#include <iostream>
using namespace std;

template <typename T>
class Registrar {
public:
    Registrar();
};

template <typename D>
struct SelfRegistrar {
   static Registrar<D> d_;
    SelfRegistrar(){
        cout << &d_ << endl;
    }
};



//for some reason I don't understand need some reference to d, opening another question...
class A : SelfRegistrar<A> { static void* foo() {return &d_;} };
class B : SelfRegistrar<B> { static void* foo() {return &d_;}};
class C : SelfRegistrar<C> { static void* foo() {return &d_;}};
class D : SelfRegistrar<D> { static void* foo() {return &d_;}};

template<typename T> struct traits { };
template<> struct traits<A> { static std::string code() {return "class A";}};
template<> struct traits<B> { static std::string code() {return "class B";}};
template<> struct traits<C> { static std::string code() {return "class C";}};
template<> struct traits<D> { static std::string code() {return "class D";}};

template <typename D>
Registrar<D> SelfRegistrar<D>::d_{};

std::string RegisterClass(const std::string & c){
    std::cout << "Registering " << c << '\n';
    return c;
}

template<typename T> void printMe() {
    std::cout << "Printing " << traits<T>::code() << '\n';
}

template <typename D>
Registrar<D>::Registrar()
{
   std::string c = traits<D>::code();
   std::cout << "Registering " << c << '\n';
}

int main() {
    // your code goes here
    printMe<B>(); 
    printMe<B>(); 
    printMe<C>(); 
    return 0;
}
0

, . / .

:

#include <string>
#include <iostream>
#include <typeinfo>

// Forward declaration for "traits" wrapper class.  If you wanted to, you 
// could to change the self_registering template to not use a wrapper class, 
// default a wrapper but allow other specializations, etc.
template<typename T> struct traits;

template<typename wrappedClass>
struct self_registering {
    self_registering() {};
    virtual ~self_registering() {};
    static bool isRegistered() { return registered; }

private:
    typedef traits<wrappedClass> WrappedClass_;
    static bool doRegistration() {
        std::cout << "Default-registering " << WrappedClass_().code() << '\n';
        return true;
    };
    static const bool registered;
};
template<typename T>
const bool self_registering<T>::registered 
         = self_registering<T>::WrappedClass_().doRegistration();

// our traits wrapper class...
template<typename T> 
struct traits : self_registering<T>   {
    static std::string code() { return typeid(T).name(); };
};

// Well, that pretty much it. Time to use it:

// a few classes we're going to (maybe) want self-registered...
class A {}; class B {}; class C {}; class D {};
class E {}; class F {}; class G {}; class H {};

// provide custom registration for class H:
template<> struct traits<H> : self_registering<H> { 
    static std::string code() { return "class H"; }

    bool doRegistration() {
        std::cout << "Private-registering " << code() << "\n";
        // do something here like H::register()...
        return true;
    };
};

template<typename T>
void printMe() {
    static bool isRegistered = traits<T>::isRegistered();
    (void)isRegistered; // shut gcc up about unused variable
    std::cout << "Printing " << traits<T>::code() << '\n';
}

int main(void)
{
    printMe<B>(); 
    printMe<B>(); 
    printMe<C>(); 

    printMe<H>(); 

    return 0;
}

, static, - - ( - , RegisterClass(), ..).

, - registered printMe(), ( , registration()). static bool, .

, ++ 11 (, , , pre-++ 11). , , - registered - .

/ VS2012 :

C:\tmp>cl /nologo /EHsc  /W4 so-misc.cpp
so-misc.cpp

C:\tmp>so-misc
Default-registering class B
Default-registering class C
Private-registering class H
Printing class B
Printing class B
Printing class C
Printing class H

gcc 4.7.2 as:

$ gcc -std=c++11 -lstdc++ -Wall -pedantic so-misc.cpp
$ ./a.out | c++filt -t
Default-registering B
Default-registering C
Private-registering class H
Printing B
Printing B
Printing C
Printing class H

Obviously, you can do other actions (hereinafter template-ize self-registeringto make the wrapper class more general than traitssome cleaning, etc.), but this may work for you as a start.

0
source

All Articles