0

I've got a program where I've got a lot of nested if/switch statements which were repeated in several places. I tried to extract that out and put the switches in a template method class, and then allow clients to overload which switch branches they wanted to specifically handle using overloading:

class TraitsA {};
class TraitsB : public TraitsA {};

class Foo
{
    bool traitsB;
public:
    // Whether or not a Foo has traitsB is determined at runtime. It is a
    // function of the input to the program and therefore cannot be moved to
    // compile time traits (like the Iterators do)
    Foo() : traitsB(false) {}
    virtual ~Foo() {}
    bool HasTraitsB() const { return traitsB; }
    void SetTraitsB() { traitsB = true; }
};

class SpecificFoo : public Foo
{
};

template <typename Client> //CRTP
class MergeFoo
{
protected:
    Foo DoMerge(Foo&, const Foo&, int, TraitsA)
    {
        // Do things to merge generic Foo
    }
public:
    // Merge is a template method that puts all the nasty switch statements
    // in one place.
    // Specific mergers implement overloads of DoMerge to specify their
    // behavior...
    Foo Merge(Foo* lhs, const Foo* rhs, int operation)
    {
        const Client& thisChild = *static_cast<const Client*>(this);

        SpecificFoo* lhsSpecific = dynamic_cast<SpecificFoo*>(lhs);
        const SpecificFoo* rhsSpecific = dynamic_cast<const SpecificFoo*>(rhs);

        // In the real code these if's are significantly worse
        if (lhsSpecific && rhsSpecific)
        {
            if (lhs->HasTraitsB())
            {
                return thisChild.DoMerge(*lhsSpecific, 
                               *rhsSpecific, 
                               operation,
                               TraitsB());
            }
            else
            {
                return thisChild.DoMerge(*lhsSpecific,
                               *rhsSpecific,
                               operation,
                               TraitsA());
            }
        }
        else
        {
            if (lhs->HasTraitsB())
            {
                return thisChild.DoMerge(*lhs, *rhs, operation, TraitsB());
            }
            else
            {
                return thisChild.DoMerge(*lhs, *rhs, operation, TraitsA());
            }
        }
    }
};

class ClientMergeFoo : public MergeFoo<ClientMergeFoo>
{
    friend class MergeFoo<ClientMergeFoo>;
    Foo DoMerge(SpecificFoo&, const SpecificFoo&, int, TraitsA)
    {
        // Do things for specific foo with traits A or traits B
    }
};

class ClientMergeFooTwo : public MergeFoo<ClientMergeFoo>
{
    friend class MergeFoo<ClientMergeFooTwo>;
    Foo DoMerge(SpecificFoo&, const SpecificFoo&, int, TraitsB)
    {
        // Do things for specific foo with traits B only
    }
    Foo DoMerge(Foo&, const Foo&, int, TraitsA)
    {
        // Do things for specific foo with TraitsA, or for any Foo
    }
};

However, this fails to compile (At least in ClientMergeFooTwo's case), saying it cannot convert a Foo& into a SpecificFoo&. Any ideas why it's failing that conversion instead of choosing the perfectly good generic overload in MergeFoo?

EDIT: Well, this psuedocode example apparently didn't do so well given how fast I tried to write it. I have corrected some of the mistakes...

Billy ONeal
  • 104,103
  • 58
  • 317
  • 552
  • Post the error, indicating the line number as well. – Nawaz Jun 30 '11 at 23:15
  • @Nawaz: I can't -- this is not the original code and I cannot post the original code here. It is failing to compile the call to `DoMerge` and it's failing due to implicit conversions. – Billy ONeal Jun 30 '11 at 23:43
  • Does your example actually fail on your box? I just tested it with VS2010 and I got no error. – zneak Jul 01 '11 at 00:05

4 Answers4

2

Any ideas why it's failing that conversion instead of choosing the perfectly good generic overload in MergeFoo?

Yes, because of name hiding rules. If a function in the derived class has the same name as a function in the base class, the base class function is "hidden", it doesn't even look at the parameters of the involved function.

That said, the solution is easy: Make the base class version available in the derived class with a simple using MergeFoo::DoMerge in the public part.

Xeo
  • 129,499
  • 52
  • 291
  • 397
0
const thisChild& = *static_cast<const Client*>(this);

I couldn't understand this? Where is the type (or the variable)? Did you mean this:

const Client & thisChild = *static_cast<const Client*>(this);

And in the following

SpecificFoo* rhsSpecific = dynamic_cast<const SpecificFoo*>(rhs);

there is mismatch in const-ness, as in the target you forgot const.

Nawaz
  • 353,942
  • 115
  • 666
  • 851
  • You are correct on all counts. My crappy thrown-together parody of the real code was thrown together too quickly :( – Billy ONeal Jun 30 '11 at 23:46
0
class ClientMergeFooTwo : public MergeFoo<ClientMergeFoo>

This could be the cause of the problem.

Puppy
  • 144,682
  • 38
  • 256
  • 465
0

Could use a little more info on where it's failing, but it looks like in order to do what you mean to do, you need to be calling Client::DoMerge() instead of just calling DoMerge(), when in the public Merge function of MergeFoo.

Nathan Monteleone
  • 5,430
  • 29
  • 43