design pattern using C++11

Hi all,

I would like to pose a design pattern to discussion that is achieved in two different ways. Imagine the situation where you have an interface "IFoo" (aka virtual) and implementations "FooA", "FooB", ... that inherit and implement "IFoo". Now you want the user (of the library you are creating, thus user of the source code) to use the "FooA", "FooB", ... but you dont want the user to have to mess with all the different "FooX"s. Instead you want to create a "wrapper" / "top" "Foo" which shall be used by the users and that "Foo" shall handle one of the underlying implementations "FooA", "FooB", ... that is chosen upon creation of "Foo".

Below two ways to achieve this. The first example uses a shared pointer to handle an instance of on one the special "FooA", "FooB", ... and implements all the same methods as "FooA", "FooB" .. since it also inherits "IFoo". But its' methods are just calling the underlying "FooX"'s methods.
The second example uses a templated "Foo" that inherits from one of the "FooA", "FooB" and thus provides intrinsically all the methods of the underlying "FooX".
Both compile using "g++ -std=c++0x -Wall example.cpp"

I would like to hear any thoughts on both ways to reach this. Thanks!

example1:
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
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
#include <iostream>
#include <memory>

using namespace std;

/**
 * The interface Foo
 */
class IFoo {
public:

    virtual void foo() const = 0;

};

/**
 * First specific implementation
 */
class FooA : public IFoo {
public:

    void foo() const {
        cout << "Implementation specific foo A!!" << endl;
    }

};

/**
 * Second specific implementation
 */
class FooB : public IFoo {
public:

    void foo() const {
        cout << "Implementation specific foo B!!" << endl;
    }

};


/**
 * The foo class that can be FooA, FooB, ...
 */
class Foo : public IFoo {

    std::shared_ptr<IFoo> impl_ptr;

    Foo(shared_ptr<IFoo> impl)
        : impl_ptr(impl)
    {}

public:

    static Foo create(int impl) {
        if (impl == 1) {
            shared_ptr<IFoo> temp(new FooA());
            return Foo(temp);
        } else {
            shared_ptr<IFoo> temp(new FooB());
            return Foo(temp);
        }
    }

    void foo() const {
        impl_ptr->foo();
    }

};

int main(int argc, char **args) {

    Foo foo1 = Foo::create(1);
    foo1.foo();
    Foo foo2 = Foo::create(2);
    foo2.foo();

    return 0;
}


example2:
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
56
57
58
59
60
61
62
63
64
65
66
#include <iostream>
#include <memory>

using namespace std;

/**
 * The interface Foo
 */
class IFoo {
public:

    virtual void foo() const = 0;

};

/**
 * First specific implementation
 */
class FooA : public IFoo {
public:

    void foo() const {
        cout << "Implementation specific foo A!!" << endl;
    }

};

/**
 * Second specific implementation
 */
class FooB : public IFoo {
public:

    void foo() const {
        cout << "Implementation specific foo B!!" << endl;
    }

};

enum TFOO
{ FOO_A, FOO_B };

/**
 * The foo class that can be FooA, FooB, ...
 */
// DUMMY default class without content
template<TFOO T>
class Foo 
{};
// REAL class with content if FooA case
template<>
class Foo<FOO_A> : public FooA
{};
// REAL class with content if FooB case
template<>
class Foo<FOO_B> : public FooB
{};

int main(int argc, char **args) {
    Foo<FOO_A> foo_a = Foo<FOO_A>();
    foo_a.foo();
    Foo<FOO_B> foo_b = Foo<FOO_B>();
    foo_b.foo();

    return 0;
}
want to create a "wrapper" / "top" "Foo" which shall be used by the users and that "Foo" shall handle one of the underlying implementations "FooA", "FooB", ... that is chosen upon creation of "Foo"

A reference to IFoo and a pointer (unique/shared/what-have-you) to IFoo satisfy these requirements.

Based on your first example, you're looking for a value-semantic type-erasing wrapper (similar to std::function). Based on your second example, you're sort of maybe looking for static polymorphism.

What is the intended use case of Foo?
thanks for your interest first of all!


A reference to IFoo and a pointer (unique/shared/what-have-you) to IFoo satisfy these requirements.

a reference or pointer to only "IFoo"? That would be kind of pointless for the user since its all virtual.

I think the best way to describe what Im looking for, as you correctly point out, is the intended use. So the scenario is that we write a library and the "user" will be another programmer using it. In particular he would be using the different FooA, FooB, ...
Instead we want to provide him with a single "Foo" that he is using without caring whether its actually a "FooA" or "FooB" or any other implementation of "IFoo".
It wouldn't be pointless since they would be able to call all of the virtual methods that the FooX's implement, which seems to be exactly what you want.
a reference or pointer to only "IFoo"? That would be kind of pointless for the user since its all virtual.

A pointer? Being pointless? Surely you jest.

Your first implementation is basically the thing you say is pointless, except you're wrapping the pointer.

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
#include <iostream>
#include <memory>
#include <cstdlib>
#include <ctime>

using namespace std;

struct IFoo {
    virtual void foo() const = 0;
};

struct FooA : public IFoo {
    void foo() const {cout << "FooA::foo()\n";}
};

struct FooB : public IFoo {
    void foo() const {cout << "FooB::foo()\n";}
};

enum FOO_TYPE
{
    FOO_A, FOO_B
};

std::unique_ptr<IFoo> get_a_foo()
{
    using ptr_type = std::unique_ptr<IFoo>;

    switch (std::rand() % 2)
    {
    case FOO_A: return ptr_type(new FooA);
    case FOO_B: return ptr_type(new FooB);

    // or, if your compiler supports the C++14 make_unique:
    // case FOO_A: return make_unique<FooA>();
    // case FOO_B: return make_unique<FooB>();
    default: return nullptr;
    }
}

int main(int argc, char **args)
{
    std::srand(std::time(0));

    for (unsigned i = 0; i < 10; ++i)
    {
        auto foo_thing = get_a_foo();

        foo_thing->foo();
        std::cout << '\n';
    }
}


http://ideone.com/IF0Asz
Topic archived. No new replies allowed.