Separation of internal and external interfaces

Apr 11, 2010 at 12:53pm
Hi all.

I am currently writing some sort of library or framework. I have two classes, say CFoo and CBar. Their properties can be described as follows:

1. Users of the framework are meant to derive their own classes from these two. The user has to implement some virtual methods and is given some helper methods (at the moment only one getter) by the classes.

2. Both classes have a lot of common functionality, so there is a common base class CBase. The interface of this base class is strictly internal and can change without any notification and should not be exposed to the frameworks user.

3. There are some internal classes, which use both CBar and CFoo and need to know about the inheritance structure.

I'm wondering how to implement this. My first approach was like this, using private inheritance:
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
class CBase
{
public:
    virtual void doSomething() = 0;

    // internal interface used by CFoo and CBar
};

class CFoo : private CBase
{
public:
    virtual void doSomethingElse() = 0;
    // Public interface for the user

private:
    void doSomething() { ... };
};


class CBar : private CBase
{
public:
    virtual void doSomethingElse() = 0;
    // Public interface for the user

private:
    void doSomething() { ... };
};


This approach has the disadvantage, that any (framework internal) class needing to know about the inheritance relationship needs to be a friend of (potentially) both CBar and CFoo.

Is there any elegant way to achieve the structure I want to have? Maybe over using some sort of aggregation or putting another inheritance layer above my classes?

Thanks in advance,
TheBear
Last edited on Apr 11, 2010 at 12:54pm
Apr 11, 2010 at 5:02pm
Out of curiosity, may I ask you to explain what the classes are actually modelling? The names Foo and Bar are less than descriptive.

My initial thought is that perhaps you could inherit an interface from a base class publicly and separate the common implementation such that it can be inherited privately. Do you have to hide the fact that CFoo and CBar have a common base class from the external users?
Apr 11, 2010 at 6:36pm
Hi,

its meant to become a framework for implementing large object data models, which are lazy updated.

So you have a (rather large) graph of objects, and modifications to this graph are done by Events. Furthermore, all this is timed, using a discrete time model, and the execution time of an event may depend on the current state of some objects.

In the terminus from the first post, CBar models an Object and CFoo models an event. The common base class is modeling an updatable, as both Objects and Events need to be updated, and the functionality for doing so is nearly the same.

I don't have to hide anything from external users, but i would rather do so - at least the user should not "know" about the methods the common base class provides for internal use. Of course, he may know about them, but iwant to strongly discourage the use of them to prevent reliance on the functionality of these methods.

By your suggestion, did you mean something like the following?
1. Rename CFoo and CBar to CFooImpl and CBarImpl, respectivly.
2. Inherit new classes CFoo and CBar privately from the implementation classes.
3. Implement simple (inlined) "Call through"-Methods to the real implementations.

That would indeed be a reasonable way to solve the problem, my only concern is efficiency... but when i think about it, there should be no noticable performance impact. At least when the compiler optimization hits in and inlines ever call through method.
Apr 12, 2010 at 7:10am
You can try use delegation instead of inheritance,
for example:
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
//forward declaration
class CBase;

class CFoo
{
private:
    CBase base_delegate;

    // Private class implementation
    // (change infreqently)
    void doMagic();

protected:
    // Protected interface for user use
    // (never or rarely change)
    virtual void doSometingElse();

public:
    // Public interface for the user
    // (never change!)
    void doSomethingMagic(){
	    doMagic();
	    doSometingElse();
	    userFunction();
    };

    virtual void userFunction() = 0;
};

class CBar
{
private:
    CBase base_delegate;

public:
    void doNonMagic();
};

void CFoo::doMagic();
{
    base_delegate.doBlackArt();
}

void CBar::doNonMagic()
{
    base_delegate.doBlackArt();
}

// Hide in CBase.cpp:
class CBase : public CBaseBase, private CBaseInterface // any form of inheritance or without
{
// internal interface used by CFoo and CBar
// can change without any notification
// any implemenation and interface as you wish
};
Apr 12, 2010 at 10:57am
Hi,

if i use delegation in this way, other internal classes (which use CBases interface) do not know about the inheritance relationship, as i only include Foo.h and Bar.h for them... so that does not solve the problem (Point 3). Internal classes might be needing to convert a CBase to a CFoo or CBar.
Apr 12, 2010 at 11:56am
convert a CBase to a CFoo or CBar

This is bad idea(if you mean downcast), who taught you that?
If you really want to convert CBase to CFoo or CBar it is not required derivative from CBase.
Maybe there is no need for class casting ever? Events and objects are different things enough.
And what you mean by
know about the inheritance structure

Apr 12, 2010 at 12:11pm
You could move the common stuff to a separate class hierarchy and add a getter into your original hierarchy.

1
2
3
4
5
6
7
8
9
10
11
12
// internal usage only. Defined somewhere not accessible to normal users
class CInternHelperBase;

class CFoo
{
public:
    // can be called by everyone, but only internal classes know the complete type.
    CInternHelperBase* getHelper();

    virtual void doSomethingElse() = 0;
    ...
};


Implementation of CFoo:
1
2
3
4
5
6
7
8
9
10
11
12
#include "CInternalHelperBase.h"   // header is in project-only folder and not shipped to your users

class CFooHelper : public CInternalHelperBase
{
public:
    virtual void doSomething() { ... }
};

CInternHelperBase* CFoo::getHelper()
{
    return new CFooHelper;
}


You should consider using std::shared_ptr, boost::shared_ptr, std::unique_ptr or std::auto_ptr for the CFooHelper for less hazzle with memory management.

Ciao, Imi.
Last edited on Apr 12, 2010 at 12:12pm
Apr 12, 2010 at 5:40pm
Hi,

Thanks for the suggestions.

@boolivar: With "need to know about the inheritance structure" i meant, that i want some internal classes to have methods like
void method(CBase& toDoSomethingWith),
and i want to call these methods with instances of CFoo and CBar. To be able to do this, the caller needs to know that CFoo is inherits from CBase, otherwise it is a compile time error (as it should be). Downcasts (or something similar) are needed sometimes to improve efficiency, but i agree, its nothing to be used to often.

For the moment i have solved the problem as as follows:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class CBase
{
public:
    virtual void doSomething() = 0;
};

class CFooImpl : public CBase
{
public:
    virtual void doSomething() { ... }
    virtual void doSomethingElse() = 0;
};

class CFoo : private CFooImpl
{
public:
    virtual void doSomethingElse() = 0;
};

Internally i work only with CFooImpl, except in interfaces to the end user.
Apr 13, 2010 at 8:03am
Look, with private inheritance you can't use upcast from CFoo anymore, but upcasting
is better then downcasting ;-)

For example you can get CBase delegate with simple sheme described by Imi above.
Or use friend class[es]:
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
class CFoo
{
friend class CFooManipulator;

private:
	CBase base_delegate;
};

//Hide from user's eyes in .cpp
class CFooManipulator
{
private:
	CFoo* foo;

public:
	CFooManipulator(CFoo* _foo) : foo(_foo){}
	void Manipulate(){
		foo->base_delegate.Manipulate();
	}

	CBase* BaseInstance()
	{
		return &foo->base_delegate;
	}
};

CFooManipulator fooManipulator(foo);
CBarManipulator barManipulator(bar);

//Wow! CBase is not base class!
method(fooManipulator.BaseInstance());
method(barManipulator.BaseInstance());


I'm not insist on delegation, just suggest. =)
Apr 21, 2010 at 4:51pm
Hi,

i was otherwise occupied the past week, but better a later answer than none.

Thanks for the suggestions. The first solution (see post #3) failed, when i tried to serialize the whole thing with boost::serialization, as boost::serialization can't cope with private inheritance.

Now i have implemented it in a similar way as boolivar suggestet in his last post, but as the base class is not only used to delegate calls, but there are also incoming calls of virtual methods from it, i did the following:

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
class CFooImpl : public CBase
{
    virtual void doSomething() = 0;

    void method() {/* ... */}
}

class CFoo
{
public:
    CFoo() : m_pImplementation(new CFooImplWrapper()) {}

    virtual void doSomething() = 0;
    void method()
    {
        m_pImplementation->method();
    }
private:
    CFooImplWrapper* m_pImplementation;
}

// Hidden:
class CFooImplWrapper : public CFooImpl
{
    explicit CFooImplWrapper(CFoo* pFoo) : m_pFoo(pFoo) {}

    virtual void doSomething()
    {
        m_pFoo->doSomething();
    }
private:
    CFoo* m_pFoo;
}


Now no one using CFoo "knows" about the inheritance, but everything works out fine. It also has the advantage of working correctly with my memory management approach :-)

Cheers, TheBear
Last edited on Apr 21, 2010 at 4:54pm
Topic archived. No new replies allowed.