The Marlboro Man
The Marlboro Man

Reputation: 971

Polymorphism and casting through struct with pointers

Full disclaimer first: I have not compiled this example code, nor the real code (well, at least fully deployed). I am still wrapping my head around the problem. With that in mind, say we have this class structure:

A super base class that we will use to store instances with this base in the same container and a few "Facet" classes that we will use with multiple inheritance to encapsulate common behaviour.

class Facet_A;
class Facet_B;
class Facet_C;

struct Facet_converter
{
    Facet_A * facet_a;
    Facet_B * facet_b;
    Facet_C * facet_c;
};

class Super_base
{
    public:

    virtual ~Super_base() {}
    virtual Facet_converter convert()=0;
    virtual const Facet_converter convert()const=0; //Notice this const...

};

class Facet_A
{
    private:

    int value_a;

    public:

    virtual ~Facet_A() {}
    Facet_A():value_a(0) {}
    void set_value_a(int v) {value_a=v;}
    int get_value_a() const {return value_a;}
};

class Facet_B
{
    private:

    float value_b;

    public:

    Facet_B():value_b(0) {}
    virtual ~Facet_B() {}
    void set_value_b(float v) {value_b=v;}
    float get_value_b() const {return value_b;}
};

class Facet_C
{
    private:

    char value_c;

    public:

    Facet_C():value_c('a') {}
    virtual ~Facet_C() {}
    void set_value_c(char v) {value_c=v;}
    char get_value_c() const {return value_c;}
};

All classes that derive from these will always:

The client code would do something like...

std::vector<Super_base *> v;

//Fill super base with the good stuff.

//Let's use everything that has an integer!.
for(auto sb : v)
{
    Facet_converter fc=sb->convert();
    if(fc.facet_a)
    {
        //Do something with this integer like... std::cout<<fc.facet_a->get_value_a()<<std::endl;
    }
}

//Let's use everything that has a float.
for(auto sb : v)
{
    Facet_converter fc=sb->convert();
    if(fc.facet_b)
    {
        //Do something with this float...
    }
}

//Let's use everything that has a char.
for(auto sb : v)
{
    Facet_converter fc=sb->convert();
    if(fc.facet_c)
    {
        //You get the drift...
    }
}

Horrible design apart (I've come to this point sick of visitors everywhere) this particular example is pretty much barebones, but you get what I am trying to do: casting down the hierarchy without using dynamic_cast and "enforcing" the compiler help (it would yell at me if I tried an assignment to a non-base class in the "convert" method).

So, a of fully implemented class...

class Derived_numeric:  //This one has a float and and int
    public Super_base,
    public Facet_A,
    public Facet_B
{
    ///Blah blah blah blah

    virtual Facet_converter convert()
    {
        Facet_converter result;
        result.facet_a=this;
        result.facet_b=this;
        result.facet_c=nullptr;     //Assume no constructor for the struct that initializes the method, not really the case.
        return result;
    }

    virtual const Facet_converter convert()const
    {
        const Facet_converter result;
        result.facet_a=this;        //Booom!!!. Error, const Derived_numeric can't be caster to Facet_A because... it's const.
        result.facet_b=this;
        result.facet_c=nullptr; 
        return result;
    }
}

And there's the problem, right in the const convert method. There's a const and a non const method because the client code may work with const and non const objects but there's no way the compiler is gonna let me assign a "const this" without const casting it first.

Considering that I've come with two solutions:

Both of them suffer from horrible code repetition, since the code is almost the same and only a few details change.

I've toyed with the idea of implementing only the const one, const_casting the "this" pointer and basically lying about what the method promises. Want true constness?, add the const modifier to the result of convert() and be done with it... Seems easier, but too sneaky.

My question is, can I implement this idea without basically copying and pasting the code and being sneaky?. Remember that I need both const and non const (the derived object may change its state by using the facets, or it may not).

Now, please consider that I am not looking for "Your approach is wrong" or "I don't know why you would want to do that". This is the current situation I want to deal with and learn about. I already know I can use double dispatching or I can bastardize the whole base class to contain every other possibility... I am just looking for alternatives to it.

Upvotes: 0

Views: 163

Answers (1)

user3995702
user3995702

Reputation:

You could make a const Facet_converter member of Super_base and then set it via a constructor.

class Super_base
{
protected:
    const Facet_converter implementations;

public:
    Super_base( const Facet_converter& implementations )
        : implementations( implementations ) {};
    virtual ~Super_base() {};
    const Facet_converter& convert() const { return implementations; }
};

When you implement the derived class, do:

Derived_numeric::Derived_numeric( ) : Super_base( Facet_converter( this, this, NULL ) )

You also need to add a constructor for the struct so that call is possible:

struct Facet_converter
{
    Facet_converter( Facet_A* const& a, Facet_B* const& b, Facet_C* const& c )
    {
        facet_a = a;
        facet_b = b;
        facet_c = c;
    }

    Facet_A * facet_a;
    Facet_B * facet_b;
    Facet_C * facet_c;
};

I haven't tested this using actual pointers and subclasses, so it might need some tweaks.

Upvotes: 1

Related Questions