A progressive disclosure implementation in C++

Hi,

I have been trying to implement the "Progressive Disclosure" pattern for a small testing
API with success. The wanted behaviour was to have the "plusFour()" service available for a
Base object, but not the "timesThree()". I wanted "timesThree" to be available only upon
request, that is, only after an "ext()" call.

This code actually compiles (if I add class implementations) and does what I want. the only
thing that bothers me is that cast from a Base class to its Derived class in "ext()". It seems to make
sense here but it feels weird.

Since I have not found a single example on the web on how to implement this, I was wondering:
is there a better -- safer -- way to do this?

Code for the present implementation (I have left out obvious method bodies):

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
#include<iostream>
#include<stdio.h>

class ExtBase;

class Base
    {
    public:
        Base() = delete;
        Base(int p_attr);

        int getAttr() const;

        ExtBase* ext() {return reinterpret_cast<ExtBase*>(this);}	// Unsure about this...

        void plusFour();	// adds four to the attribute

    protected:
        int m_attr;
    };

class ExtBase : public Base
    {
    public:
        void timesThree();	// Multiplies by three the attribute
    };

int main()
    {
    Base var(0);
    std::cout << "m_attr = " << var.getAttr() << std::endl;     // m_attr = 0

    var.plusFour();
    std::cout << "m_attr = " << var.getAttr() << std::endl;     // m_attr = 4

    // Access to new functionnality:
    ExtBase var1 = *(var.ext());

    std::cout << "New functionnality \"timesThree()\" enabled..." << std::endl;

    std::cout << "m_attr = " << var1.getAttr() << std::endl;    // m_attr = 4

    var1.timesThree();
    std::cout << "m_attr = " << var1.getAttr() << std::endl;    // m_attr = 12

    var1.plusFour();    // Still usable...
    std::cout << "m_attr = " << var1.getAttr() << std::endl;    // m_attr = 16

    getchar();
    return 0;
    }


Thanks!

Progressive Disclosure: http://queue.acm.org/detail.cfm?id=1071731
A base class pointer can point to derived class objects, so you can return this without casting in ext() and accept it as an ExtBase* return
Actually it is not working: since a Base is not necessarily an ExtBase (whereas the reverse is true), the compiler won't accept it.
I don't understand, ext() is a (non-virtual) method of class Base => it is invoked by a Base object => the this pointer in this context points to a Base object

ExtBase is derived publicly from Base i.e ExtBase is a Base => pointer to Base is also a pointer to ExtBase

Anyways, I don't want to hog your thread. Let's wait a while and see what other replies come in.

Edit: finally I see what OP is trying to say, sorry for the confusion
Last edited on
Because your ExtBase class contains no other data member, I think you will be OK. You are just telling the caller to treat the pointer to the Base as a pointer to an ExtBase.

If you had other members in ExtBase, the copy constructor in line 37 would put garbage into part of var1. As it is, Base and ExtBase have the same footprint, so the copy constructor in line 37 happens to work correctly.
@doug4, that is what I though. In this special case, we could say that conceptually, Base is kind of an ExtBase because, like you said, they only vary on the service they offer, not on the data (which is the basis of Progressive Disclosure as I understand it...)

Thanks
Just as a wrap up, this link [1] addresses this issue. It would seem that under some very special conditions (which are met here) C++11 defines this type of conversion.

These conditions are so strict though that I'm not sure this implementation of the progressive disclosure pattern has any practical value. If you know of another way to do it, please fell free to share it !

Ref:
[1] http://stackoverflow.com/questions/7762929/safety-of-casting-between-pointers-of-two-identical-classes
I would think that inheritance is not a good approach here. Extra classes that references the original data are more suitable for this:
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<stdio.h>

class ExtBase
    {
        int &m_attr;

public:
    ExtBase(int &attr) : m_attr{attr}
    {
    }
        void timesThree()	// Multiplies by three the attribute
        {
            m_attr *= 3;
        }
    };

class Base
    {
    public:
        Base() = delete;
        Base(int p_attr) : m_attr(p_attr)
        {
        }

        int getAttr() const
        {
            return m_attr;
        }

        ExtBase ext() {return ExtBase{m_attr};}

        void plusFour()	// adds four to the attribute
        {
            m_attr += 4;
        }

    protected:
        int m_attr;
    };


int main()
    {
    Base var(0);
    std::cout << "m_attr = " << var.getAttr() << std::endl;     // m_attr = 0

    var.plusFour();
    std::cout << "m_attr = " << var.getAttr() << std::endl;     // m_attr = 4

    // Access to new functionnality:
    ExtBase var1 = var.ext();

    std::cout << "New functionnality \"timesThree()\" enabled..." << std::endl;

    std::cout << "m_attr = " << var.getAttr() << std::endl;    // m_attr = 4

    var1.timesThree();
    std::cout << "m_attr = " << var.getAttr() << std::endl;    // m_attr = 12

    var.plusFour();    // Still usable...
    std::cout << "m_attr = " << var.getAttr() << std::endl;    // m_attr = 16

    getchar();
    return 0;
    }
@coder777 Just tested it and it works. This is definitely more suitable.

The reference trick:

1
2
3
4
5
class ExtBase
    {
        int &m_attr;
        ...
    }


very clever, Thanks!!
Note that it has its limitation: The extended object must non exceed the lifetime of its parent. Otherwise you have undefined behavior and likely a crash.
Indeed! I thought about this problem and I think I fixed it. You can have
ExtBase adopt a noncopyable behaviour:

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
#include <iostream>
#include <cstdio>

class ExtBase
    {
    public:
        // Non copyable behaviour:
        ExtBase() = delete;
        ExtBase(const ExtBase&) = delete;
        ExtBase& operator=(const ExtBase&) = delete;
        ExtBase* operator &() = delete;

        ExtBase(int &p_attr);

        ~ExtBase();

        void timesThree();

    private:
        int& m_attr;

    };
    
    class Base
    {
    public:
        Base() = delete;
        Base(int p_attr);

        ~Base();

        int getAttr() const;
        ExtBase ext();

        void plusFour();

    private:
        int m_attr;
    };

    
    int main()
    {
    int x = 0;
    ExtBase extVar(x); // Needed to try to extend scope...

        // Begin scope of var.
        {
        Base var(0);

        var.plusFour();
        std::cout << "m_attr = " << var.getAttr() << std::endl;     // m_attr = 4

        var.ext().timesThree();
        std::cout << "m_attr = " << var.getAttr() << std::endl;    // m_attr = 12

        var.plusFour();
        std::cout << "m_attr = " << var.getAttr() << std::endl;    // m_attr = 16

        ExtBase extVar {var.ext()};     // Fails: deleted!
        ExtBase extVar = var.ext();     // Fails: deleted!
        ExtBase* extVar = &var.ext();   // Fails: deleted!

        }
        // End scope of var.

    getchar();
    return 0;
    }


this gives the following output (with couts in destructors and lines 60 to 62 commented out):


Base created
m_attr = 4
ExtBase created (call to "ext()")
ExtBase deleted
m_attr = 12
m_attr = 16
Base deleted
Last edited on
Topic archived. No new replies allowed.