1

Is there anyway to get a type from a static base class method? For instance

class A
{
    static std::type_info getClassType
    {
        // What do I do here
    }
};

class B : public A
{

};

B instance;
auto my_type = instance::getClassType()

With C++'s lack of static variable overloading, I am having a hard time figuring out a way to determine class type across classes without doing a special virtual function in every child class which I am trying to avoid due to the sheer number.

David
  • 1,648
  • 1
  • 16
  • 31
  • 1
    Why do you think you need this? – Lightness Races in Orbit Mar 20 '18 at 00:51
  • 1
    Search "C++ CRTP" – Jive Dadson Mar 20 '18 at 00:53
  • @LightnessRacesinOrbit Hard to explain it all, but it boils down to there being a manager class that keeps track of one instance of a variety of classes and their states. You may use this singleton "instance" of these sub classes, but I want to be able to say MySubClass::instance() and then have it get the correct instance from the manager without having to write it in each sub class. Basically, I could do it the hard way, but I feel it should be possible to do within the base class and want to know if its possible. I am aware of many ways to write more code and get it done though. – David Mar 20 '18 at 00:56
  • `instance.getClassType()` is syntaxic sugar equivalent to `A::getClassType()`. – Jarod42 Mar 20 '18 at 01:04
  • Thanks, that was a typo – David Mar 20 '18 at 01:16
  • @Jarod42 Btw, the problem is that I don't have an instance and I am using this static method to create an instance. – David Mar 20 '18 at 01:17

4 Answers4

2

Make a class that will be a template.
Subclass from it. Pass the child type as the template parameter.
With that, base class will know the child. The T will be the child type.

Code:

#include <iostream>
using namespace std;

template<typename T> class thebase
{
  static T instance;
public:
  static T& getInstance() { return instance; }
};

template <typename T> T thebase<T>::instance;

class sub1 : public thebase<sub1> {
public: void tell() { std::cout << "hello1: " << this << std::endl; }
};

class sub2 : public thebase<sub2> {
public: void tell() { std::cout << "hello2: " << this << std::endl; }
};

int main() {
  sub1& ref1 = sub1::getInstance();
  sub1& ref2 = sub1::getInstance();
  std::cout << ((&ref1) == (&ref2)) << std::endl;
  sub1::getInstance().tell();
  sub2::getInstance().tell();
  sub1::getInstance().tell();
  sub2::getInstance().tell();
  return 0;
}

Output:

1
hello1: 0x55874bff1193
hello2: 0x55874bff1192
hello1: 0x55874bff1193
hello2: 0x55874bff1192

This kind of code pattern is sometimes called CRTP

quetzalcoatl
  • 32,194
  • 8
  • 68
  • 107
  • and yup, I forgot the typeinfo, but since it's possible to declare `static T instance;` then the type is known statically, so typeinfo has to work as well. – quetzalcoatl Mar 20 '18 at 01:29
  • I immediately suggested CRTP in the commets to the Question. I was fiddling around trying to make `getClassType` return a `const type_info&` when you posted this. That's what the OP wanted, not a `tell()`. I think I'll let you work it out. – Jive Dadson Mar 20 '18 at 02:06
2

it boils down to there being a manager class that keeps track of one instance of a variety of classes and their states. You may use this singleton "instance" of these sub classes, but I want to be able to say MySubClass::instance() and then have it get the correct instance from the manager without having to write it in each sub class.

You can implement the managed classes using CRTP ("Curiously recurring template pattern") so a base class knows the derived class types and can then delegate that information to the manager class.

Try something like this:

#include <map>
#include <typeinfo>
#include <typeindex>

class Base {
public:
    virtual ~Base() {}
};

class Manager
{
private:
    static std::map<std::type_index, Base*> m_instances;

public:
    template<typename T>
    static void addInstance(Base *inst) {
        if (!m_instances.insert(std::make_pair(std::type_index(typeid(T)), inst)).second)
            throw ...; // only 1 instance allowed at a time!
    }

    template<typename T>
    static void removeInstance() {
        auto iter = m_instances.find(std::type_index(typeid(T)));
        if (iter != m_instances.end())
            m_instances.erase(iter);
    }

    template<typename T>
    static T* getInstance() {        
        auto iter = m_instances.find(std::type_index(typeid(T)));
        if (iter != m_instances.end())
            return static_cast<T*>(iter->second);
        return nullptr;
    }
};

std::map<std::type_index, Base*> Manager::m_instances;

template<class Derived>
class A : public Base
{
public:
    A() {
        Manager::addInstance<Derived>(this);
    }

    ~A() {
        Manager::removeInstance<Derived>();
    }

    static Derived* getInstance() {
        return Manager::getInstance<Derived>();
    }
};

class B : public A<B>
{
    ...
};

class C : public A<C>
{
    ...
};

B b_inst;
C c_inst;

...

B *pB = B::getInstance();
if (pB) ...

C *pC = C::getInstance();
if (pC) ...

Live Demo

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
1

Based on your own example of code in your own answer, here's a final result working:

# include <iostream>
# include <typeinfo>

template <class T> 
class A
{
public:
    static const std::type_info& getClassType()
    {
        return typeid(T);
    }
};

class B : public A<B>
{ /* ... */ };

class C : public A<C>
{ /* ... */};

class D : public A<D>
{ /* ... */};

int main()
{
    B b; C c; D d;
    auto& b_type = b.getClassType();
    auto& c_type = c.getClassType();
    auto& d_type = d.getClassType();

    std::cout << b_type.name() << std::endl;
    std::cout << c_type.name() << std::endl;
    std::cout << d_type.name() << std::endl;
}

Output: 1B 1C 1D

R.R.C.
  • 5,439
  • 3
  • 14
  • 19
0

Someone above mentioned the CRTP or "Curiously recurring template pattern". Using this, I have not tested it, but it appears I may be able to write:

template <class T> 
class A
{
    static std::type_info getClassType
    {
        return typeid(T);
    }
};


class B : public A<B>
{

};

B instance;
auto my_type = instance::getClassType()
David
  • 1,648
  • 1
  • 16
  • 31