Typedefs in Derived Classes

I am working on a code generation tool with which I can generate Data classes and associated Manipulator classes specific to each Data class. As part of the generation tool, some common, non-generated files are provided to perform various functionality, including accessing the manipulator classes. The user is expected to interact with the Data classes and the common files, but is not expected to interact directly with the Manipulator classes.

I recently added the ability to generate derived classes to the tool. Things work out really well until trying to access a derived class manipulator from a base class reference. A VERY stripped down example of what I am trying to do is here (comments in the code are addressed in my question below):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
#include <string>
#include <iostream>

// Content similar to that found in generated Data classes
class BaseManip;
class BaseData
{
public:
    typedef BaseManip ManipType;    // Can this be made virtual?
    virtual ~BaseData() = default;
};

class DerivedManip;
class DerivedData: public BaseData
{
public:
    typedef DerivedManip ManipType; // I want this type picked up from a
                                    // BaseData* to an obj of this class.
};


// Content similar to that found in generated Manipulator classes

class BaseManip
{
public:
    void doIt(const BaseData& data)
    {
        std::cout << "doIt Base Class" << std::endl;
        doItImpl_Base_extensions(data);
    }

protected:
    virtual void doItImpl_Base_extensions(const BaseData& data) {}
};


class DerivedManip : public BaseManip
{
protected:
    virtual void doItImpl_Base_extensions(const BaseData& data)
    {
        const DerivedData& myData = dynamic_cast<const DerivedData&>(data);
        std::cout << "doIt Derived Class" << std::endl;
    }
};


// Content similar to that found in generated
// library code provided by generation tool;

template <typename TYPE>
void masterDoIt(TYPE& data)
{
    // Here I want to create a DerivedManip object when a BaseData& to a
    // DerivedData object is passed into this function.
    typename TYPE::ManipType manip;
    manip.doIt(data);
}


// User code

int main()
{
    DerivedData data;

    std::cout << "Derived masterDoIt" << std::endl;
    masterDoIt(data);
    std::cout << std::endl;

    std::cout << "Base masterDoIt" << std::endl;
    BaseData& baseRef = data;
    masterDoIt(baseRef);
    std::cout << std::endl;
}


When masterDoIt is called with a BaseData& argument, masterDoIt uses the ManipType from BaseData rather than the ManipType from DerivedData, even though the object is truly of type DerivedData.

I know that I could provide a virtual getManipObject function of some sort rather than the typedef, but I was wondering, is there were a way to make the typedef "virtual" so that masterDoIt will create the correct Manipulator when a BaseData& to a DerivedData object is passed in?

Note, the project is currently using a C++11 compiler.

Edit: simplified code for Manip classes.
Last edited on
Types are compile-time entities; for a polymorphic typedef, the polymorphism has to be compile-time polymorphism. For example CRTP (in this case, with a common non-templated base class with a virtual destructor).
@JLBorges,

Thanks for the response. I think I understood most of what you wrote. I read up on CRTP because I was (and still am) only vaguely familiar with the concept.

Types are compile-time entities; for a polymorphic typedef, the polymorphism has to be compile-time polymorphism.


This is what I figured. I think that what you are saying is that at compile time, BaseData is a concrete class, it can only have 1 ManipType typedef, and that's what is used by the instantiation of masterDoIt<>().

For example CRTP


I think you are saying that CRTP is an example of compile time polymorphism. But even if I were to used CRTP, a single call to masterDoIt would only instantiate 1 copy of the function, so I would still be left with a single ManipType object in line 57 when called at line 74.

(in this case, with a common non-templated base class with a virtual destructor)


I'm unclear here. Does "in this case" refer to my code or a potential implementation that could demonstrate what you are trying to describe? I don't understand how a non-templated base class could be used in CRTP.

Regardless, I needed a run-time solution. this is what I came up with:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
class BaseData
{
public:
    typedef BaseManip ManipType;
    typedef BaseManip RootManipType;

    virtual ~BaseData() = default;
    virtual std::unique_ptr<RootManipType> getManip();
};

class DerivedData: public BaseData
{
public:
    typedef DerivedManip ManipType; // I want this type picked up from a
    typedef BaseData::RootManipType RootManipType;

    virtual std::unique_ptr<RootManipType> getManip();
};

std::unique_ptr<BaseData::RootManipType> BaseData::getManip()
{
    return std::unique_ptr<RootManipType>(new ManipType);
}

std::unique_ptr<DerivedData::RootManipType> DerivedData::getManip()
{
    return std::unique_ptr<RootManipType>(new ManipType);
}

template <typename TYPE>
void masterDoIt(TYPE& data)
{
    auto manipPtr = data.getManip();
    manipPtr->doIt(data);
}



Because my code is generated, these typedefs and functions are automatically added to my classes without any user input.

Edit: forgot updated masterDoIt.
Last edited on
Something along these lines, perhaps:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
#include <iostream>
#include <memory>
#include <type_traits>
#include <vector>
#include <typeinfo>

struct abstract_base ; // base class for all data objects
struct base_manip // base class for all manips (pure interface)
                  // (a common non-templated base class with a virtual destructor)
{
    virtual ~base_manip() = default ;
    virtual void do_it( abstract_base& ab ) = 0 ;
    using pointer = std::unique_ptr<base_manip> ;
};

struct abstract_base // base class for all data objects (pure interface)
{
    virtual ~abstract_base() = default ;
    virtual base_manip::pointer manip() const = 0 ;
};

// CRTP implementation for derived class data objects
template < typename DERIVED > struct base : abstract_base
{

    virtual base_manip::pointer manip() const override
    {
        // instantiate (defalt initialise) a manip of type DERIVED::manip
        static_assert( std::is_default_constructible<typename DERIVED::manip>::value, "not default constructible" ) ;
        return std::make_unique< typename DERIVED::manip >() ;
    }
};

struct derived : base<derived>
{
    // each derived class defines its own manipulator
    struct manip : base_manip
    {
        void do_it( abstract_base& b )
        {
            std::cout << "derived::manip::do_it\n" ;

            // throw if do_it is called with an object of an incompatible type
            derived& d = dynamic_cast<derived&>(b) ;

            // do something on d
            d.str += '!' ;
        }
    };

    std::string str = "derived" ;
};

struct another_derived : base<another_derived>
{
    // each derived class defines its own manipulator
    struct manip :base_manip
    {
        void do_it( abstract_base& b )
        {
            std::cout << "another_derived::manip::do_it\n" ;
            another_derived& a = dynamic_cast<another_derived&>(b) ;
            // do something on a
            ++a.i ;
        }
    };

    int i = 20 ;
};

void do_it( const std::vector<abstract_base*>& objects )
{
    for( auto& ptr : objects ) if(ptr)
    {
        #ifndef NDEBUG
            std::cout << "type " << typeid(*ptr).name() << " : " ;
        #endif // NDEBUG
        auto manip = ptr->manip() ;
        if(manip) manip->do_it(*ptr) ;
    }
}

int main()
{
    derived a, b, c ;
    another_derived d, e, f ;

    // derived class does not define its own manip
    // objects of this class use the base class manip (another_derived::manip)
    struct more_derived : another_derived {} g, h, i;

    do_it( { &a, &d, &g, &b, &e, &h, &c, &f, &i } ) ;
}

http://coliru.stacked-crooked.com/a/0a9b611ed49e86e1
https://rextester.com/ECLKH17283
@JRBorges,

Thank-you. I think I understand now. Nice explanation.

Topic archived. No new replies allowed.