Skip to content

Instantly share code, notes, and snippets.

@hostilefork
Last active December 27, 2015 06:19
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save hostilefork/7280477 to your computer and use it in GitHub Desktop.
Save hostilefork/7280477 to your computer and use it in GitHub Desktop.
Conceptual example for Proxy/Facade pattern to submit to http://codereview.stackexchange.com/questions/33713/
//
// Proxy/facade pattern idea.
// See http://codereview.stackexchange.com/questions/33713/
//
#include <iostream>
#include <vector>
#include <memory>
#include <cstdlib>
using std::unique_ptr;
using std::shared_ptr;
using std::make_shared;
using std::vector;
using std::is_base_of;
using std::is_convertible;
using std::move;
using std::cout;
//
// Forward definitions
//
class Foo;
//
// FooPrivate
//
// Raw handle we never give directly to client. Structure may live in a
// memory mapped file; and adding data members to it would be misleading.
// Declared "final" and has no virtual methods.
//
class FooPrivate final {
friend class Foo;
template<class> friend class Reference;
template<class> friend class Owned;
template<class> friend struct std::default_delete;
private:
FooPrivate (int value) : _value (value) { }
virtual ~FooPrivate () { }
private:
static unique_ptr<FooPrivate> create(int value) {
return unique_ptr<FooPrivate> (new FooPrivate (value));
}
public:
FooPrivate * consume(unique_ptr<FooPrivate> && other) {
_value += other->_value;
FooPrivate * result (other.release());
_consumed.push_back(result);
return result;
}
void setValue(int value) { _value = value; }
int getValue() const { return _value; }
private:
int _value;
vector<FooPrivate *> _consumed;
};
//
// Context
//
// Placeholder for context information that we want our wrapper to carry
// along with every handle. Shown as a flag here for simplicity.
//
class Context final {
friend class Foo;
template<class> friend class Reference;
template<class> friend class Owned;
private:
// Note: can't use make_shared when constructor is private
Context (bool valid) : _valid (valid) { }
private:
bool _valid;
};
//
// Reference<FooType>
//
// Proxy/facade pattern; creates subtypes that can add additional accessor
// methods built on top of the basic ones offered by Foo. Class parameter
// is the accessor which should be publicly derived from Foo.
//
template<class FooType>
class Reference
{
friend class Foo;
private:
Reference (FooPrivate * fooPrivate, shared_ptr<Context> context) {
static_assert(
is_base_of<Foo, FooType>::value,
"Reference parameter must be publicly derived from Foo"
);
_foo.setInternals(fooPrivate, context);
}
public:
Reference (Foo & foo) : _foo (foo) { }
template<class OtherFooType>
Reference (Reference<OtherFooType> & other) {
static_assert(
is_convertible<OtherFooType, FooType>::value,
"Cannot construct Reference from an incompatible Foo Type"
);
_foo.setInternals(other._fooPrivate, other._context);
}
FooType * operator-> () { return &_foo; }
FooType const * operator-> () const { return &_foo; }
FooType & getFoo() { return _foo; }
FooType const & getFoo() const { return _foo; }
private:
FooType _foo;
};
//
// Owned<FooType>
//
// Combination between unique_ptr and the proxy/facade abilities of
// Reference.
//
template<class FooType>
class Owned
{
friend class Foo;
private:
Owned (
unique_ptr<FooPrivate> && fooPrivate, shared_ptr<Context> context
) {
static_assert(
is_base_of<Foo, FooType>::value,
"Owned template parameter must be publicly derived from Foo"
);
_foo.setInternals(fooPrivate.release(), context);
}
unique_ptr<FooPrivate> extractFooPrivate()
{
unique_ptr<FooPrivate> result (_foo._fooPrivate);
_foo.setInternals(nullptr, nullptr);
return result;
}
// Disable default copying and assignment operators
Owned (Owned<FooType> const & foo);
Owned<FooType> operator= (Owned<FooType> const & foo);
public:
static Owned<FooType> create(int value) {
return Owned<FooType> (
FooPrivate::create(value),
shared_ptr<Context> (new Context (true))
);
}
template<class OtherFooType>
Owned (Owned<OtherFooType> && other) {
static_assert(
is_convertible<OtherFooType, FooType>::value,
"Cannot move-construct Owned from an incompatible Foo Type"
);
_foo.setInternals(
other.extractFooPrivate().release(),
other._foo._context
);
}
template<class OtherFooType>
Owned<FooType> operator= (Owned<OtherFooType> && other) {
static_assert(
is_convertible<OtherFooType, FooType>::value,
"Cannot move-assign Owned from an incompatible Foo Type"
);
return Owned<FooType> (other.extractFooPrivate(), other._context);
}
FooType * operator-> () { return &_foo; }
FooType const * operator-> () const { return &_foo; }
FooType & getFoo() { return _foo; }
FooType const & getFoo() const { return _foo; }
private:
FooType _foo;
};
//
// Foo
//
// Basic wrapped abstraction of the raw handle with context. User code can
// legally use this directly, but Reference<Foo> is encouraged for
// consistency with other accessor patterns like Reference<FooDerived>.
//
class Foo {
template<class> friend class Reference;
template<class> friend class Owned;
template<class> friend struct std::default_delete;
private:
void setInternals(FooPrivate * fooPrivate, shared_ptr<Context> context) {
// Separate function for setting internals, used by Reference
// and Owned to make sure relevant members are set (these are
// private and not directly accessible by Foo derived classes)
_fooPrivate = fooPrivate;
_context = context;
}
protected:
Foo () {
setInternals(nullptr, nullptr);
}
private:
Foo (FooPrivate * fooPrivate, shared_ptr<Context> context) {
setInternals(fooPrivate, context);
}
FooPrivate const & getFooPrivate() const {
if (not _context->_valid) {
throw "Attempt to dereference invalid Foo handle.";
}
return *_fooPrivate;
}
FooPrivate & getFooPrivate() {
if (not _context->_valid) {
throw "Attempt to dereference invalid Foo handle.";
}
return *_fooPrivate;
}
public:
template<class FooType>
Reference<FooType> consume(Owned<FooType> && foo) {
return Reference<FooType> (
getFooPrivate().consume(move(foo.extractFooPrivate())),
shared_ptr<Context> (new Context (false))
);
}
void setValue(int value) { getFooPrivate().setValue(value); }
int getValue() const { return getFooPrivate().getValue(); }
private:
FooPrivate * _fooPrivate;
shared_ptr<Context> _context;
};
//
// DoublingFoo
//
// A dumb accessor helper just to show the point.
//
class DoublingFoo : public Foo {
public:
int getDoubleValue() const {
return Foo::getValue() * 2;
}
Reference<Foo> consumeHelper(int value) {
return consume(Owned<Foo>::create(value * 2));
}
};
//
// A dumb basic test.
//
int main()
{
auto parent (Owned<DoublingFoo>::create(10));
auto child (Owned<Foo>::create(20));
parent->consumeHelper(30);
Reference<Foo> childReference (parent->consume(move(child)));
cout << "The value is " << parent->getDoubleValue() << "\n";
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment