tags:

views:

74

answers:

5

first of all i think its a crapy design , but im trying to prove a point.

i want to count all the instances of derivers from my class, im trying to do it like so:

.h file:

#ifndef _Parant
#define _Parant

#include<map>

class Parant
{
public:
    Parant();
    virtual ~Parant();
    static void PrintInstances();

private:
    static void AddInstance(const char* typeName);
    static std::map<const char*, int> InstanceCounter;
};

#endif

.cpp file:

#include "Parant.h"
#include <typeinfo>
#include <iostream>

using namespace std;

Parant::Parant()
{
    AddInstance(typeid(this).raw_name());
}


Parant::~Parant()
{
}


std::map<const char*, int> Parant::InstanceCounter;

void Parant::AddInstance(const char* typeName)
{
    InstanceCounter[typeName]++;
}


void Parant::PrintInstances()
{
    for(map<const char*,int>::iterator i = InstanceCounter.begin(); i != InstanceCounter.end(); i++)
    {
        cout << " typename: " << i -> first << " ;;" ;
        cout << " count: " << i -> second << endl ;
    }

}

i have to inheritors that look like this(the cpp contains empy implemintations):

#pragma once
#include "parant.h"
class ChildA :
    public Parant
{
public:
    ChildA(void);
    virtual ~ChildA(void);
};

and this is the main function:

int main()
{
    ChildA a;
    ChildB b;
    ChildA a1;

    Parant::PrintInstances();
....

the result i get is:

 typename: .PAVParant@@ ;; count: 3

help, why doesnt it work?


edit: i changed it to AddInstance(typeid(*this).raw_name()); of course it still doesnt work, though now i understand why... can i get it to work?

+5  A: 

typeid(*this) in a constructor just yields the constructor's class (you had it typeid(this) but that's wrong anyway since it will just give you the type_info of a pointer). That's considered the dynamic type of the object during construction.

Another difference there is that virtual functions called during construction won't end up in the derived class, but in the class where the call is made during construction.

Johannes Schaub - litb
ive added stuff in the edit. can i get it to work?
Hellfrost
@Hellfrost: It's clear from the answer that `typeid` won't work as expected in the constructor. I guess the only way is to separately call another function *after* the object has been created.
casablanca
A: 

You need to call the AddInstance from the child class constructors for the "this" pointer to be of child class type. But that imposes on each child class that they have to implement this "interface" of yours.

C++ doesn't support reflection like Java so this can't be done easily in a generic fashion.

David
boost does something similar with macros, so it is possible. anyhow the this pointer is pointing at the correct object with the correct v_table pointer so this should be possible
Hellfrost
A: 

You can make it work by passing class name from derived class like below.

class Parent
{
public:
   Parent(const char* pClassName) //Gets called for every derived object with
   {                              //corresponding derived class name as parameter.
       AddInstance(pClassName);  //Instance count will be incremented here.
   }
};

class ChildA: public Parent
{
public:
    ChildA()
      : Parent("ChildA") //Pass the child class name to Parent.
    {
    }
};
class ChildB: public Parent
{
public:
    ChildB()
      : Parent("ChildB")
    {
    }
};

Rest of the code remains same as what you provided.

bjskishore123
+1  A: 

Johannes explains why this doesn't work.

As a possible workaround, you can pass a pointer to the derived class instance to the Parent constructor using the initialization list:

struct ChildA : Parent 
{
    ChildA() : Parent(this) { }
};

However, in the Parent constructor, if you dereference this pointer, typeid will still tell you that its dynamic type is Parent. You can, however, make the Parent constructor a template:

struct Parent
{
    template <typename T>
    Parent(T* x)
    {
       AddInstance(typeid(T).raw_name());
    }

    // ...
};

This constructor template will be instantiated for each derived class type, and T will be the correct type of the derived class.

This approach becomes more difficult with multiple levels of inheritance and it requires that you pass the this pointer to the base class constructor explicitly, but it's one way to "solve" the problem.

James McNellis
ive copied exactly what you offered, why does it wants this: ChildA::ChildA(void) : Parant(*this) . to compile
Hellfrost
@Hellfrost: If your `Child` class looks exactly like that and the `Parent` constructor looks exactly like that, there should be no errors; are you sure your `Parent` constructor takes a `T*` and not a `T`?
James McNellis
@James: I feel, Passing unconstructed this pointer from derived constructor to base constructor is dangerous. What do you say ?
bjskishore123
@bjskishore123: It's not inherently wrong. Some compilers (Visual C++, for example) will issue a warning about it because obviously it is potentially unsafe. As long as the base class does not expect that the pointer refers to a constructed object, there's no problem.
James McNellis
A: 

Does this suffice your need? Uses CRTP

map<string, int> m;

template<class T> struct Base{
    Base(){
        m[typeid(T).name()]++;    // potentially unsafe
    }
};

struct Derived : Base<Derived>{
};

struct AnotherDerived : Base<AnotherDerived>{
};

int main(){
    Derived d1, d2;
    AnotherDerived d11, d21;
}
Chubsdad