tags:

views:

362

answers:

4

Hello,

I want a virtual class like this:

class Configuration 
{
public:
    enum EPromptId;

    virtual CString getPrompt( EPromptId promptId ) = 0;
private:
};

So that each derived configuration can have its own set of EPromptIds

 class Configuration1 : public Configuration
{
public:
    enum EPromptId{
        epid_HappyBirthday
    };

    CString getPrompt( EPromptId promptId ){
        return "";
    }
private:
};

class Configuration2 : public Configuration
{
public:
    enum EPromptId{
        epid_JummpingJehoshaphat
    };

    CString getPrompt( EPromptId promptId ){
        return "";
    }
private:
};

This fails as each class needs to implment a virtual function with a Configuration::EPromptId parameter (not a Configuration1::EPromptId or Configuration2::EPromptId as in this code).

Is it possible to get the base class to recognise the parameter type but define the values differently in each derived class (perhaps not using enums, but keeping it strongly typed, i.e. not using an int).

EDIT : I want two different configurations for two different 'applications'. The prompts could be held in a db table but each 'application' will have it's own table. A pointer to the base configuration class is contained in a class which interfaces to some hardware (i.e. which does the actual displaying). The hardware is an io device that can be used to request and receive user input. When the hardware class is created it will be passed a pointer to the correct configuration class and so display the right prompts when requested.

+3  A: 

First: You want an abstract class. (An abstract class is one that has at least one pure virtual function. A virtual base class is a class that's virtual derived from.)

Then: No, what you want is not possible. You cannot forward-declare an enum and define it later, let alone define it differently. And even if you could: what would happen if someone passes epid_HappyBirthday to Configuration2 which doesn't know about it?

I suggest you explain to us what you want to do (instead of whether the way you thought you found how to do this works) and maybe someone can come up with an idiom to solve your problem.

sbi
Please see edit
Patrick
Passing epid_HappyBirthday to Configuration2 would be a bug, plain and simple.
Patrick
+1  A: 

You can't mix compile-time type checking with runtime virtual function resolving.

You can have generic

class Configuration 
{
public:
    virtual CString getPrompt( int promptId ) = 0;
private:
};

and define two member functions in derivatives:

class Configuration1 : public Configuration
{
public:
    enum EPromptId{
        epid_HappyBirthday
    };

    CString getConfiguration1Prompt( EPromptId promptId ){
        return "";
    }
    virtual CString getPrompt( int promptId )
    {
      return getConfiguration1Prompt(static_cast<EPromptId>(promptId));
    } 
private:
};

class Configuration2 : public Configuration
{
public:
    enum EPromptId{
        epid_JummpingJehoshaphat
    };

    CString getConfiguration2Prompt( EPromptId promptId ){
        return "";
    }
    virtual CString getPrompt( int promptId )
    {
      return getConfiguration2Prompt(static_cast<EPromptId>(promptId));
    } 
private:
};

If you want to ensure valid promptId you should check it manually in runtime in child classes.

Anyway this approach is not useful because to use generic getPrompt() function you'll need to know which child class you're using to get access to its EPromptId.

pingw33n
Be aware, that in this sample epid_HappyBirthday and epid_JummpingJehoshaphat probably will have the same value (0). You rather should create a separate enum in the base class, that contains all values of the enum, that are used in the derived classes. Thats not very object oriented, but its the only way, i know to solve such problems.
RED SOFT ADAIR
The getPrompt is pure virtual so you'll always know which child class is being called at runtime
Patrick
@ StefanWoe, They will definitely have the same value, that's what I want
Patrick
This wouldn't really work as the caller would have use enums from either Configuration1 or Configuration2. I.e. the caller would have to be aware of the sub-classes, which defeats the point of using inheritance in this case. The enums would have to be combined into a single type and moved to the base class. Even then i suspect this isn't what the OP actually wants.
jon hanson
Oops, beat me to it...
jon hanson
@Patrick: If you want to have different prompts for a specified set of prompt ids per each child configuration class its more correctly to do as StefanWoe suggests.
pingw33n
@pingw33n: each child configuration will have its own db table or file linking a prompt id to some text, therefore each child classes enums need to start at 0. If I add a new prompt for one configuration I don't want to have to think about the enums of other configurations.
Patrick
@Patrick: You want the enums to have the SAME value? Why?
RED SOFT ADAIR
@Stefan: see the edit to the question
Patrick
A: 

My c++ is a bit rusty but can't you do something like

struct EPromptId {
    EPromptId() mId(sId++) { }
    operator int() { return mId; }
    friend static bool operator==(EPromptId lhs, EPromptId rhs) { 
        return lhs.mId == rhs.mId;
    }
private:
    int mId;
    static int sId;
};

struct configuration1 {
    static const EPromptId epid_HappyBirthday;
    static const EPromptId epid_xxx;

    CString getPrompt(EPromptId promptId ){
        if (promptId == epid_HappyBirthday)
            return "";
        else if (promptId == epid_xxx)
    }
}

// somewhere else
EPromptId configuration1::epid_HappyBirthday;
EPromptId configuration1::epid_xxx;

If you want to control each id manually, just add a and int constructor
EPromptId(int id) mId(id) { }

and change the initialization to

EPromptId configuration1::epid_HappyBirthday = 1;
EPromptId configuration1::epid_xxx = 5;
adrianm
I dont think, that this has any use. The static variable exists only once for all derived classes - and it will be incremented each time the constructor is called.
RED SOFT ADAIR
Oops, missed the initialization and added manual numbering
adrianm
+1  A: 

You might be able to get most of what you want with templates... as long as you weren't hoping to keep around an collection of Configuration* and access them that way (you weren't, were you? Because you'd need to know their types to know what to pass anyway....)

template< typename ENUM_TYPE >
class Configuration 
{
public:
    virtual CString getPrompt( ENUM_TYPE promptId ) = 0;
private:
};

then

enum EPromptId{
    epid_HappyBirthday
};

class Configuration1 : public Configuration< EPromptId >
{
public:
    CString getPrompt( EPromptId promptId ){
        return "";
    }
private:
};
Caleb Huitt - cjhuitt