3

Let's suppose I have something like:

class A 
{
  public:
     A(A* owner)
     {
        m_owner=owner;
        if (dynamic_cast<B*>(this))
        {
          m_id=sm_bid; 
          sm_bid++;
        }
        else
        {
          m_id=sm_aid;
          sm_aid++;
        }
     }
  private:
    int m_id;
    A*  m_owner;
    static int sm_aid;
    static int sm_bid;
};

A::sm_aid=0;
A::sm_bid=0;

class B: public A
{
    B(A* owner) : A(owner) {}
};

Unfortunately, the dynamic_cast is unable to catch that it is a B object (when instantiated). This sound logical (since the constructor in the superclass is called before to call the constructor in the subclass. Is there a way to achieve such functionality?

n. m. could be an AI
  • 112,515
  • 14
  • 128
  • 243
vpuente
  • 882
  • 7
  • 15
  • How about passing a `std::type_info` to `A::A` ? – Quentin Jan 13 '17 at 10:27
  • 1
    Not like that, no - since `A`s constructor is invoked before `B`s (i.e. before there is any information that a `B` is actually being constructed). In any event, why would you want to do this? - it is completely the opposite of good technique - such as compliance with the Liskov substitution principle. – Peter Jan 13 '17 at 10:28
  • 3
    This doesn't look like a good design. Why would a base class have any knowledge about its child? – freakish Jan 13 '17 at 10:28
  • How about make ctor a template and with `std::enable_if` conditionally enable ctor only for derived objects; something like `template::type>A(T* owner)` – MRB Jan 13 '17 at 10:34
  • The idea is to automatically initialize members in superclass according each subclass (for example number of objects of class B and class A, to assign a unique identifier). – vpuente Jan 13 '17 at 10:35
  • Better way would for `B`s constructor to pass required values to `A`s constructor. Or (maybe) make the members protected, and assign values to them within `B`s constructor. When designing a `B`, one will generally know how one wants the base `A` to be initialised. – Peter Jan 13 '17 at 10:49
  • 1
    If you get type of derived class via template argument, you can do whatever you want in base class according to the template argument. Is `A` an abstract type?(according to your information it must be) – MRB Jan 13 '17 at 10:52
  • Yep... A is an abstract type. Not shown in the example. The template approach might impact performance? – vpuente Jan 13 '17 at 11:05
  • The "template approach" is a compile-time approach. If done right, it all happens at compile time, so will not affect program performance. – Peter Jan 13 '17 at 12:10

4 Answers4

2

Your design looks convoluted and almost certainly could be implemented without base needing to know it's derived.

However, it is the holy right to shoot oneself in the foot with C++ :) Therefore, you can try using Curiously Recurring Template Pattern (CRTP) to achieve your goals (polymorphic behavior in the constructor via static polymorphism).

Here is the quick and dirty sketch:

#include <iostream>
#include <type_traits>

struct Owner
{};

struct B;

template <class Derived>
struct A : public Owner
{
    A(Owner*)
    {
        if (std::is_same<Derived, B>::value)
            std::cout << "called from derived B" << std::endl;
        else
            std::cout << "not called from derived B\n" << std::endl;
    }
};

struct B : public A<B>
{
    B(Owner* owner) : A<B>(owner) {}
};

int main()
{
    A<void> a(nullptr);
    B b(&a);
}

Output:

not called from derived B
called from derived B

Note: I noticed that you are using static variables for counting. Please note that each template instantiation will have its own static variable!

Community
  • 1
  • 1
AMA
  • 4,114
  • 18
  • 32
  • Thanks for your answer. I changed a bit the code to avoid structs. – vpuente Jan 13 '17 at 12:18
  • @vpuente if this or any other answer has solved your question please consider accepting it (by clicking on a check-mark below votes). This indicates that you've found a solution and gives some reputation to both the answerer and yourself. There is no obligation to do this. – AMA Jan 13 '17 at 12:20
  • @vpuente I am happy if the answer was helpful :) I've edited the post to add a note on static variables. – AMA Jan 13 '17 at 12:29
2

Firstly, for dynamic_cast to work you need to have at least one virtual method in the base class (usually the destructor is made virtual since you often need that for other reasons).

Secondly, while constructing the A subobject, in A's constructor all virtual functions will refer A's class definition (even if you are in fact constructing A as a subobject of B) and dynamic_cast will say that this is not a type B.

This is because the object is not an object of type B until the completion of B's constructor, and so any and all checks to try to determine if it's a type B will say it's not because the object is in fact not a type B (yet).

I hope this clears up some confusion for you.

EDIT:
I just noticed the last paragraph under your question where you instead ask for a solution to this.

Without knowing more about the problem it will be difficult to really help you with achieving this functionality. My suggestion is to not do that, perhaps use another class altogether to assign id's to your objects rather than trying to have A do it.

If it really must be in A then perhaps this could be of help:

struct A
{
  template <class FinalType>
  A(A* owner, FinalType *){...}//use std::is_same to check if `B` or not
};

struct B : A
{
  B(A * owner):A(owner, this){}
};
SirGuy
  • 10,660
  • 2
  • 36
  • 66
1

If A is an abstract type you can use tag based overloading technique.

class B;
class C;

class A
{
public:
    template<class Derived>
    struct my_derived
    {
        using type = Derived;
    };

protected:
    explicit A(my_derived<B>)
    {
        // Do whatever you want when derived is B
    }

    explicit A(my_derived<C>)
    {
        // Do whatever you want when derived is C
    }

private:

};

class B : A
{
public:
    B() : A(A::my_derived<B>())
    {
    }
};

class C : A
{
public:
    C() : A(A::my_derived<C>())
    {
    }
};

Also another approach would be template based programming as AMA suggest. But Although Templates are powerful feature in C++ they have their own problems. They can't be implemented in a cpp file. They cause code bloat that can affect performance due to bigger code size that will cause higher rate of instruction cache miss.

So I think this approach is better than RTTI that has run time performance impact and you have to declare one virtual function in your base class, and also template based approach.

MRB
  • 3,752
  • 4
  • 30
  • 44
  • It work just fine (and it fits to use derived classes bellow B or C). I need to figure out how to make Swig to digest this :). Certainly more elegant that the template approach. – vpuente Jan 13 '17 at 15:26
0

It seems like this works just fine:

#include <iostream>
#include <type_traits>

class Owner
{};

class B;

template <class Derived>
class A : public Owner
{
    public:
    A(Owner* owner)
    {
        m_owner=owner;
        if (std::is_same<Derived, B>::value)
            std::cout << "called from derived B" << std::endl;
        else
            std::cout << "not called from derived B\n" << std::endl;
    }
    private:
    Owner* m_owner;
};

class B : public A<B>
{
    public:
    B(Owner* owner) : A<B>(owner)
    {}
};

int main()
{
    A<void> a(nullptr);
    B b(&a);
}

Kudos to AMA. Anything can be done in C++

vpuente
  • 882
  • 7
  • 15