LinuxQuestions.org
Visit the LQ Articles and Editorials section
Go Back   LinuxQuestions.org > Forums > Non-*NIX Forums > Programming
User Name
Password
Programming This forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.

Notices



Reply
 
Search this Thread
Old 09-14-2010, 01:05 PM   #1
cbh2000
LQ Newbie
 
Registered: Sep 2010
Distribution: openSUSE 11.3
Posts: 14

Rep: Reputation: 0
Unhappy Access inherited member from base class? (C++)


I have a problem. This class is a base class for a basic enum handler:

Code:
#define ENUMOBJECT_BEGIN_ELEMENTS enum Element {
#define ENUMOBJECT_END_ELEMENTS ElementCount, DefaultElement, NoElement };

struct EnumObject
{
    ENUMOBJECT_BEGIN_ELEMENTS
        /* Just default elements defined by macro... */
    ENUMOBJECT_END_ELEMENTS

    EnumObject();
    ...
    EnumObject& operator =(Element e);
    ...

private:
    Element __ELEMENT__;
};
This is a base class that is meant to be inherited, like this:

Code:
class Enum : public EnumObject
{
public:

    ENUMOBJECT_BEGIN_ELEMENTS
        EnumElement1,
        EnumElement2,
        ...,
    ENUMOBJECT_END_ELEMENTS
};
However, when I run into code like this...
Code:
Enum e;
e = Enum::EnumElement1;
...obviously there is a problem, since the inherited member function "EnumObject& operator =(Element e);" is from "EnumObject" and does not recognize "Enum" members (this makes sense).
However, is there a way that I could use a base class to modify an inherited class's data?

Last edited by cbh2000; 09-14-2010 at 01:07 PM.
 
Old 09-14-2010, 02:07 PM   #2
cbh2000
LQ Newbie
 
Registered: Sep 2010
Distribution: openSUSE 11.3
Posts: 14

Original Poster
Rep: Reputation: 0
Band-Aid Solution

I came up with a solution:
Code:
#define ENUMOBJECT_BEGIN(objectName) \
    struct objectName \
    { \
        enum Element

#define ENUMOBJECT_END(objectName) ; \
        \
        objectName(Element e = No ## objectName) { __ELEMENT__ = e; } \
        virtual ~objectName() {} \
        \
        operator Element() { return __ELEMENT__; } \
        operator int() { return __ELEMENT__; } \
        objectName& operator =(Element e) { __ELEMENT__ = e; return *this; } \
        \
        bool isValid() const { return __ELEMENT__ < objectName ## Count; } \
        \
    private: \
        Element __ELEMENT__; \
    };

#define ENUMOBJECT_DEFAULT(objectName, defaultValue) \
    objectName ## Count, No ## objectName, Default ## objectName = defaultValue

ENUMOBJECT_BEGIN(ErrorCode)
{
    Normal = 0,
    GeneralError = 1,

    ENUMOBJECT_DEFAULT(ErrorCode, GeneralError)
}
ENUMOBJECT_END(ErrorCode)

int main()
{
    ErrorCode e = ErrorCode::DefaultErrorCode;
    return e; // returns 1
}
It is not very flexible, but it is simple...
 
Old 09-14-2010, 02:36 PM   #3
Sergei Steshenko
Senior Member
 
Registered: May 2005
Posts: 4,481

Rep: Reputation: 453Reputation: 453Reputation: 453Reputation: 453Reputation: 453
The thread name itself suggests you are trying to do a wrong thing. I.e. base classes are not supposed to know their children. I.e. your overall design/architecture appear to be wrong.
 
Old 09-14-2010, 03:03 PM   #4
dugan
Guru
 
Registered: Nov 2003
Location: Canada
Distribution: distro hopper
Posts: 5,002

Rep: Reputation: 1558Reputation: 1558Reputation: 1558Reputation: 1558Reputation: 1558Reputation: 1558Reputation: 1558Reputation: 1558Reputation: 1558Reputation: 1558Reputation: 1558
Quote:
Originally Posted by cbh2000 View Post
Is there a way that I could use a base class to modify an inherited class's data?
This sentence describes polymorphism.

Code:
class Base
{
public:
    int x;
    virtual void initialize();
};

void Base::initialize()
{
    x = 1;
}

class Derived: public Base
{
public:
    int y;
    void initialize();
};

void Derived::initialize()
{
    Base::initialize();
    y = 1;
}

Base *base = new Derived();
ptr->initialize();
delete base;
Presto. The base class has modified the inherited class' data.

Last edited by dugan; 09-14-2010 at 03:05 PM.
 
Old 09-14-2010, 03:42 PM   #5
johnsfine
Guru
 
Registered: Dec 2007
Distribution: Centos
Posts: 5,142

Rep: Reputation: 1127Reputation: 1127Reputation: 1127Reputation: 1127Reputation: 1127Reputation: 1127Reputation: 1127Reputation: 1127Reputation: 1127
I hate the excessive use of #define in your first version. But the far more excessive abuse of #define in the second version is far worse.

When you want to do something like that with #define, you can almost always do something better with templates.

Quote:
Originally Posted by cbh2000 View Post
However, is there a way that I could use a base class to modify an inherited class's data?
The most powerful way to do that is for the base class to be a template which takes the derived class as its template parameter.

When you first see this, it will look impossibly circular. But C++ allows it and C++ compilers handle it correctly and it is a very powerful method to inject common functionality into multiple classses even when that functionality must be customized to each class.

To make this easier for you to understand, I'll leave in the ugly #defines of your first version.

I'm not taking the time to test this, so there might be a few details wrong. I've used this approach many times, so the approach is valid even if some details are wrong

Code:
#define ENUMOBJECT_BEGIN_ELEMENTS enum Element {
#define ENUMOBJECT_END_ELEMENTS ElementCount, DefaultElement, NoElement };

template <class Actual>
struct EnumObject
{
    typedef typename Actual::Element Element;
    Actual& cast() {return *static_cast<Actual*>(this);}
    Actual& operator =(Element e)
    {
        __ELEMENT__ = e;
        return cast();
    }
    ...

private:
    Element __ELEMENT__;
};

class Enum : public EnumObject<Enum>
{
public:

    ENUMOBJECT_BEGIN_ELEMENTS
        EnumElement1,
        EnumElement2,
        ...,
    ENUMOBJECT_END_ELEMENTS
};

Last edited by johnsfine; 09-14-2010 at 03:47 PM.
 
Old 09-16-2010, 01:37 AM   #6
cbh2000
LQ Newbie
 
Registered: Sep 2010
Distribution: openSUSE 11.3
Posts: 14

Original Poster
Rep: Reputation: 0
Indeed! I had just come to the same solution myself, and was about to go to bed after congratulating myself but stumbled upon your reply while checking my email.

Code:
template<typename Inherited>
struct EnumBase
{
    enum Element
    {
        Count,
        Default,
        None
    };

    EnumBase(Inherited::Element e = None);

private:
    Element e;
};
However, I really like your idea of using the base's functions as the inherited's. Thank you!

Quote:
I hate the excessive use of #define in your first version. But the far more excessive abuse of #define in the second version is far worse.
Aww! I've been fond of macros lately!

Maybe I need to read my "Big C++" book, again...

Last edited by cbh2000; 09-16-2010 at 01:46 AM.
 
Old 09-16-2010, 09:19 AM   #7
johnsfine
Guru
 
Registered: Dec 2007
Distribution: Centos
Posts: 5,142

Rep: Reputation: 1127Reputation: 1127Reputation: 1127Reputation: 1127Reputation: 1127Reputation: 1127Reputation: 1127Reputation: 1127Reputation: 1127
Quote:
Originally Posted by cbh2000 View Post
I had just come to the same solution myself
A few questions about your version:

Is there some purpose to defining enum Element{...} in the base class? So far as I understand, you never want to use that version of Element. I think my approach:
typedef typename Actual::Element Element;
is much more effective, because it makes Element in the base class be the actual type of Element in the actual class.

With your version of Element in the base class, what is the purpose of
private: Element e;
That Element is the wrong type to use for values of Actual::Element

What compiler are you using? I don't think the following line of your code is valid:
EnumBase(Inherited::Element e = None);
Older compilers will recognize that Inherited::Element is a type name and be able to parse that, but the C++ standard says they shouldn't, so newer compilers should not be able to parse that. Also, you have made None the wrong type, so that shouldn't compiler either.

But I am impressed you thought of the main idea of inheriting from a base class that is templated by the inherited class. Had you seen that trick before? I would never have started using that myself if I hadn't seen it in other code. I would have assumed it was too circular to compile.

Last edited by johnsfine; 09-16-2010 at 09:22 AM.
 
Old 09-16-2010, 11:16 AM   #8
cbh2000
LQ Newbie
 
Registered: Sep 2010
Distribution: openSUSE 11.3
Posts: 14

Original Poster
Rep: Reputation: 0
Actually, yes, I do want to use the base class' Element enum, because only then may I provide two default elements: "None" (Unassigned) and "Default" without having to provide them in the inherited class.

I didn't compile the code when I put it here (I encountered the problems you have mentioned and fixed them when trying to compile), and I'm using GCC 4.5. And, you are right, it won't compile:
Code:
../untitled/enumobject.h: In instantiation of ‘EnumObject<Enum>’:
../untitled/enumobject.h:20:1:   instantiated from here
../untitled/enumobject.h:7:38: error: invalid use of incomplete type ‘class Enum’
../untitled/enumobject.h:19:7: error: forward declaration of ‘class Enum’
Ugh!

I'm becoming frustrated with C++'s lack of ingenuity... Is there a way to force the compiler to define Enum later? (extern keyword only works on variables or functions.)

I'm tempted to create my own preprocessor, but it wouldn't be "standards compliant."

Last edited by cbh2000; 09-16-2010 at 11:33 AM.
 
Old 09-16-2010, 12:06 PM   #9
cbh2000
LQ Newbie
 
Registered: Sep 2010
Distribution: openSUSE 11.3
Posts: 14

Original Poster
Rep: Reputation: 0
Forget it. I'm just doing this from now on:
Code:
class Argument
{
public:
    enum ID
    {
        Help,
        ...
        IDCount,
        NoID,
        DefaultID = NoID
    };

    enum Type
    {
        Option,
        ...
        TypeCount,
        NoType,
        DefaultType = Parameter
    };

    ...
};

int main()
{
    Argument help(Argument::Help, Argument::Option, ...);
    ...
    return 0;
}
I was hoping for something like this...
Code:
int main()
{
    Argument help(Argument::ID::Help, Argument::Type::Option, ...);
    ...
    return 0;
}
...but it is easiest to use macros to achieve the affect, as in this code:
Code:
class Argument
{
public:
    ENUM(ID)
        Help,
        ...
    DEFAULT_ENUM(ID, NoID)

    ENUM(Type)
        Option,
        ...
    DEFAULT_ENUM(Type,  Parameter)
    ...
};
Which expands to:
Code:
class Argument
{
public:
    struct ID
    {
        enum Element
        {
            Help,
            ...
            IDCount,
            NoID,
            DefaultID = NoID
        };

        ID(Element e = NoID) { this->e = e; }

        operator Element() { return e; }
        operator int() { return e; }
        ID& operator =(Element e) { this->e = e; return *this; }

        bool isValid() const { return e < IDCount; }

    private:
        Element e;
    };

    struct Type
    {
        enum Element
        {
            Option,
            ...
            TypeCount,
            NoType,
            DefaultType = Parameter
        };

        Type(Element e = NoType) { this->e = e; }

        operator Element() { return e; }
        operator int() { return e; }
        Type& operator =(Element e) { this->e = e; return *this; }

        bool isValid() const { return e < TypeCount; }

    private:
        Element e;
    };
    ...
};
...which is not as efficient as simply using an enum.

Last edited by cbh2000; 09-16-2010 at 12:09 PM.
 
  


Reply

Tags
access, c++, inheritance, member


Thread Tools Search this Thread
Search this Thread:

Advanced Search

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
Overriding inherited member functions in C++ vtable? disruptive Programming 14 08-18-2010 07:52 AM
[SOLVED] Python n00b: promoting a derived class to base class? CoderMan Programming 2 03-11-2010 02:46 PM
CPP: Is it possible to use the members of an inherited class? RHLinuxGUY Programming 5 04-30-2007 03:48 AM
Does derivated class inherit base class destructor (constructor)? kornerr Programming 2 08-23-2006 09:05 AM
c++ : regarding (inheritence)base class and derived class edreddy Programming 6 07-31-2002 07:33 PM


All times are GMT -5. The time now is 05:54 AM.

Main Menu
Advertisement
My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
identi.ca: @linuxquestions
Facebook: linuxquestions Google+: linuxquestions
Open Source Consulting | Domain Registration