topsframework / tops

ToPS Framework: http://topsframework.github.io/tops/doc/api/
GNU General Public License v2.0
5 stars 1 forks source link

Generators #4

Closed renatocf closed 9 years ago

renatocf commented 9 years ago

Hey @igorbonadio,

I was thinking about ToPS' design and our ideas of having an Evaluator (storage of data and interface for inference methods) and a Trainer (builder to create models bit by bit). In this way, methods choose still would be accessed by model classes. So, what do you think about creating a class Generator, which chooses a new sequence for a given model? I don't think it's as useful as evaluators and trainers, but it would be, at least, symmetric in all uses.

A short example with this intermediate classes:

auto hmm = HiddenMarkovModel::trainer(training_set)->trainML(/* training arguments */);

auto labeling = hmm->decodableEvaluator(sequence)->labeling(Labeling::bestPath);

auto newSequence = hmm->generator()->choose(size, phase);

What do you think?

igorbonadio commented 9 years ago

I think it is a good idea! That way, a model will only define its structure.

Evaluator knows how to infer and Generator knows how to generate data. Let's do it.

renatocf commented 9 years ago

This classes will be very similar to Evaluator. However, I think we have to solve some problems before. For example: as I looked the choose methods in our model classes, we had the following signature:

  virtual Symbol   choosePosition(const Sequence &s, unsigned int i,
                                  unsigned int phase = 0) const;
  virtual Sequence chooseSequence(Sequence &s, unsigned int size,
                                  unsigned int phase = 0) const;

I believe we need to do some refactorings:

  1. Remove the argument s from choosePosition. I'm not sure about this one, but in all implementations (all that are not missing), it's never used.
  2. Remove the argument s from chooseSequence. The sequence is returned twice - once by the argument and another time by the return value. And I believe the latter is more clear.
  3. Rename both methods to choose. If sequence s is really necessary in choosePosition, we'll have different parameters and will be able to use polimorphism.

But, before anything, we need to test all methods choose. And I'm not sure how we can do that. I downloaded and compiled ToPS 1.0, but I didn't find an app to test sequence generation (and I don't know how to test them on hand). So I believe we should start by that.

igorbonadio commented 9 years ago

@renatocf

1) s is used in VLMC. 2) As chooseSequence depends on choosePosition, s is used inchooseSequence 3) I think choosePosition will behave the same way that evaluatePosition

And I agree. It is better add test before we touch in this code... Take a look at the Random header. I think we need to change it a little, because the seed is always randomically generated... It should have a way to set a fixed seed.

renatocf commented 9 years ago

@igorbonadio,

I changed the default random number generator to Marsenne Twister. Accordingly to Wikipedia, it's the most used algorithm among programming languages. This implementation, different from default_random_engine, is platform independent. I also implemented a function to reset the engine - which enable the tests to be independent. At least we have basic guarantees when refactoring the code.

renatocf commented 9 years ago

Ah, and to clarify, this page explains how <random> library works. This paragraph is specially interesting:

The choice of which engine to use involves a number of tradeoffs: the linear congruential engine is moderately fast and has a very small storage requirement for state. The lagged Fibonacci generators are very fast even on processors without advanced arithmetic instruction sets, at the expense of greater state storage and sometimes less desirable spectral characteristics. The Mersenne twister is slower and has greater state storage requirements but with the right parameters has the longest non-repeating sequence with the most desirable spectral characteristics (for a given definition of desirable).

I think Mersenne Twister has this advantage not listed there: it's platform independent. For our production code, what do you think it would be better to use?

renatocf commented 9 years ago

Another thing: do you believe that, as VLMC uses the sequence s, we could change the choose methods signature from:

virtual Symbol   choosePosition(const Sequence &s, unsigned int i,
                                unsigned int phase = 0) const;
virtual Sequence chooseSequence(Sequence &s, unsigned int size,
                                unsigned int phase = 0) const;

to:

virtual Symbol   choose(const Sequence &s,
                        unsigned int i,
                        unsigned int phase = 0) const;

virtual Sequence choose(unsigned int size,
                        unsigned int phase = 0) const;

for all ProbabilisticModels?

renatocf commented 9 years ago

@igorbonadio,

I've already answered the previous question (and, further, changed the methods).

Now, I'm attempting to create a class DecodableEvaluator, which will have nice stuff Following our philosophy of always return through objects (and never for non-const-references or pointers in parameters), I'd like to change chooseSequences signature from:

virtual void chooseSequences(Sequence &xs,
                             Sequence &ys,
                             unsigned int size) const;

to:

virtual std::pair<Sequence, Sequence> choose(unsigned int size,
                                             unsigned int phase = 0) const;

I believe a pair is enough, given knowing which sequence would be x or y is meaningless (at least as far as I understood). So, accessing .first and .second in the return value seems OK.

However, it creates a problem: we cannot have functions overloaded by return values... How do you think we could deal with that? Change the refactored name is the most obvious solution. But, which name should we choose? For example: jointProbabilityOf would be a good name for the overloaded probabilityOf in Evaluator. Is there something similar to choose? I mean, is there a special meaning in choosing two functions together?

igorbonadio commented 9 years ago

1) pair

virtual std::pair<Sequence, Sequence> choose(unsigned int size,
                                             unsigned int phase = 0) const;

In fact, x and y are an observation and a label sequence. But I think pair is a generic and good solution.

2) I think a good name could be chooseJointSequence. What do you think?

renatocf commented 9 years ago

So, y is a label sequence. But what type of label sequence? As in Labeling? (which has Method::bestPath and Method::posteriorDecoding?). Because in this case, perhaps there is a better meaning for what this function generates. Do you know if there is any relation between these things?

igorbonadio commented 9 years ago

It generates a sequence of observations (x) that has a segmentation (y)

renatocf commented 9 years ago

@igorbonadio. After thinking about our last conversation, I created a new proposal for changes in the definition of Labeling, Generator and Evaluator. Take a look and tell me what you think about it.

First, the basic probabilistic model methods would become:

// I would introduce the alias "Probability" to standardize
// the use of "double" and "float" among methods. It also
// increases the meaning of method signatures
virtual Probability evaluate(const Sequence &sequence,
                             unsigned int pos,
                             unsigned int phase = 0) const;

virtual Symbol   choose(const Sequence &context,
                        unsigned int pos,
                        unsigned int phase = 0) const;

virtual Sequence choose(unsigned int size,
                        unsigned int phase = 0) const;

Then, for decodable models, we can define a sequence + label version:

virtual Probability evaluateLabeling(const Labeling<Sequence> &sequence,
                                     unsigned int i) const;

virtual Labeling<Symbol>   chooseLabeling(const Sequence &context,
                                          unsigned int pos) const;

virtual Labeling<Sequence> chooseLabeling(unsigned int size) const;

In this way, Labeling would become a simple template class that would have the following methods:

template <typename T>
class Labeling {
 private:
  bool has_probability;
  T observation;
  T label;
  Probability probability;

 public:
  const T& observation() const;
  const T& label() const;

  bool has_probability() const;
  Probability probability() const;
}

So, a Labeling is the association of an observation, a label and its joint probability (if it exists). I'm not sure what is the best method for testing if there is a probability - or if we could have an EvaluatedLabeling/Estimated<Labeling>, subclass/decorator of Labeling (just throwing some ideas). Anyway, what do you thing of this general design?

igorbonadio commented 9 years ago

I thing it is great. But why is Labeling a template class?

renatocf commented 9 years ago

I thought it would be better because we can specify if we have a labeling of a symbol or a labeling of a sequence (as they are different things, and I believe we should have different constraints in each of them).

What do you think about the idea of an Estimation class? It could have two attributes - a probability and an estimate - in a similar way of labeling. It would be also a template, and they would work in a way similar to having "decorators" around our class (if the language was dynamic, I'd implement it delegating all methods in an incremental way. However, I see no easy way of doing it in C++).

The implementation would be something like this:

template<typename T>
class Labeling {
 private:
  T observation;
  T label;

 public:
  T& observation();
  T& label();
}

template<typename T>
class Estimation {
 private:
  T estimated;
  Probability probability;
 public:
  T& estimated();
  T& probability();
}

So, return values would be something like:

virtual Estimation<Labeling<Symbol>>   chooseLabeling(const Sequence &context,
                                                      unsigned int pos) const;

virtual Estimation<Labeling<Sequence>> chooseLabeling(unsigned int size) const;

They would be used as this:

auto generator = hmm->generator();

auto estimation = generator->chooseLabeling(some_size);

estimation.probability();
estimation.estimated().label();
estimation.estimated().observation();

What do you think? Is that a lot? I'm not sure if we need all of this. I'll start implementing the refactorings I proposed in the last comment, and then e can see what is better.

igorbonadio commented 9 years ago

Hummm It seems interesting. And I like how to code looks

estimation.probability();
estimation.estimated().label();
estimation.estimated().observation();
renatocf commented 9 years ago

Nice. Perhaps I'll try to find more interesting names (I specially didn't like estimated), but it'll be very similar to this.

renatocf commented 9 years ago

I decided to do a small TODO list to help me to organize myself:

renatocf commented 9 years ago

@igorbonadio,

I was thinking about the design of Generator's. Right now, we planned to have just one method generator that would give us an instance of Generator class, with methods to choose standalone/labeled symbols/sequences. Instead of that, I though it could be nice to have a slightly different design: in ProbabilisticModel, we'd have a method sequenceGenerator, which would give us a Generator<Sequence> with a method choose. At the same time, all subclasses of DecodableModel would also have a labelingGenerator that returns a Generator<Labeling> with an equal method choose. The usage would then be:

hmm->sequenceGenerator()->choose(size, phase);
hmm->labelingGenerator()->choose(size, phase);

The problem that arises from that is: how can the templates make their double dispatch and delegate to the right class methods? Perhaps I could create specializations for Sequence and Labeling. This is an alternative to inheritance. What do you think is the best alternative?

renatocf commented 9 years ago

Better than my last suggestion: the lazy template method generation solve the problem. We just need to check (as in SimpleEvaluatorImpl and CachedEvaluatorImpl) our trait is_decodable<T>:

If we have the class:

template<typename Model>
class Generator {
  Sequence chooseSequence(unsigned int size, unsigned int phase) {
    /* choose sequence */
  }

  Labeling chooseLabeling(unsigned int size, unsigned int phase) {
    return chooseLabelingImpl(size, phase);
  }

  template<typename T = Model>
  Labeling chooseLabelingImpl(unsigned int size, unsigned int phase,
                              not_decodable<T>* dummy) {
    /* throw error */
  }

  template<typename T = Model>
  Labeling chooseLabelingImpl(unsigned int size, unsigned int phase,
                              is_decodable<T>* dummy) {
    /* create labeling */
  }
}
renatocf commented 9 years ago

@igorbonadio,

Here it is the reformulated version of the minimal example I showed you earlier. As this example looked very nice. And I think it give us a behavior very similar to a client-server architecture.

Basically, Foo, FooImpl, SimpleFooImpl and CachedFooImpl are the implementation of a front-end for using back-end classes Bar, BarDerived and BarReusing. In order to use method, first we get an instance of the Foo front-end from one of the back-ends. All state is kept in the front-end, while execution is made only in the back-end.

The trickiest thing is that we have a composite pattern in the back-end and a bridge pattern in the front-end: the first because of the nature of the problem we're solving (integrator probabilistic models behavior like a composite) and the second because it's the only way we can generate code accordingly to the classes (a workaround for not having virtual template functions). Finally, both things communicate in a client-server way. Nevertheless, in the end of the day, they're just a bunch of design patterns together.

Now, to make things easier, I listed how methods are called from main until algorithms are executed:

////////////////////////////////////////////////////////////////////////////////
//                                                                            //
//                            foo = model->foo()                              //
//                                                                            //
////////////////////////////////////////////////////////////////////////////////
//                                                                            //
//                               foo->method()                                //
//                                    \/                                      //
//                             fooImpl->method()                              //
//                                    \/                                      //
//                           fooImpl->methodImpl()                            //
//                                    \/                                      //
//                        model->simpleMethod(fooImpl)                        //
//                                    \/                                      //
//                      model->simpleMethodImpl(fooImpl)                      //
//                                                                            //
////////////////////////////////////////////////////////////////////////////////

And here it's the working code:

// Standard headers
#include <memory>
#include <iostream>
#include <type_traits>

/*
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
 -------------------------------------------------------------------------------
                              IMPLEMENTATION HELPER
 -------------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
*/

/* MACRO GENERATE_HAS_MEMBER **************************************************/

// This macro is aimed to create a member detector: a class and type trait
// that can be used to check when an attribute/method exists in a class.
// As there is an infinite number of names, it's impossible to create a
// standard type trait only with the resources the language provides. Given
// that, it's necessary to create a macro to automatically generate all
// classes and alias from a name given as parameter.

// The following macro creates:
// - A template class that uses SFINAE and multiple inheritance to decide if
//   the member exists in the class. In case it does, the inner class `Derived`
//   has two `member`s and its size is the same is a char[2]. This information
//   is to store in `RESULT` a boolean that indicates a member exists.
// - A struct inheriting from `std::integral_constant`, which have a trait
//   compliant with STL.
// - Two alias `has_##member` and `no_##member` to selectively create
//   methods by applying SFINAE on its parameters.

#define GENERATE_HAS_MEMBER(member)                                         \
                                                                            \
template <typename T>                                                       \
class HasMember_##member                                                    \
{                                                                           \
private:                                                                    \
    using Yes = char[2];                                                    \
    using  No = char[1];                                                    \
                                                                            \
    struct Fallback { int member; };                                        \
    struct Derived : T, Fallback { };                                       \
                                                                            \
    template<typename U> static No&  test (decltype(U::member)*);           \
    template<typename U> static Yes& test (U*);                             \
                                                                            \
public:                                                                     \
    static constexpr bool RESULT                                            \
      = sizeof(test<Derived>(nullptr)) == sizeof(Yes);                      \
};                                                                          \
                                                                            \
template<typename T>                                                        \
struct has_member_##member                                                  \
    : public std::integral_constant<bool, HasMember_##member<T>::RESULT> {  \
};                                                                          \
                                                                            \
template<typename Model>                                                    \
using has_##member = typename                                               \
  std::enable_if<has_member_##member<Model>::value                          \
                 && std::is_constructible<Model>::value, bool>::type;       \
                                                                            \
template<typename Model>                                                    \
using no_##member = typename                                                \
  std::enable_if<!has_member_##member<Model>::value                         \
                 || !std::is_constructible<Model>::value, bool>::type;

// Generate the above structure for the following list of methods:
GENERATE_HAS_MEMBER(simpleMethod)
GENERATE_HAS_MEMBER(cachedMethod)

/*
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
 -------------------------------------------------------------------------------
                               HIERARCHY FRONT-END
 -------------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
*/

/* CLASS FooImpl **************************************************************/

// Forward declaration
class FooImpl;

// Alias
using FooImplPtr = std::shared_ptr<FooImpl>;

/**
 * @class FooImpl
 * Interface for implementation of Foo front-end
 */
class FooImpl : public std::enable_shared_from_this<FooImpl> {
 public:
  // Virtual methods
  virtual void method() = 0;
};

/* CLASS SimpleFooImpl ********************************************************/

// Forward declaration
template<typename T>
class SimpleFooImpl;

// Alias
template<typename T>
using SimpleFooImplPtr = std::shared_ptr<SimpleFooImpl<T>>;

/**
 * @class SimpleFooImpl
 * Simple implementation of Foo front-end
 */
template<typename T>
class SimpleFooImpl
    : public std::conditional<!std::is_void<typename T::base>::value,
               SimpleFooImpl<typename T::base>, FooImpl>::type {
 public:
  // Alias
  using TPtr = std::shared_ptr<T>;

  // Constructor
  SimpleFooImpl(TPtr t = TPtr())
      : _t(std::move(t)) {
  }

  // Overriden methods
  void method() override {
    methodImpl();
  }

 private:
  // Instance variables
  TPtr _t;

  // Concrete methods
  template<typename U = T>
  void methodImpl(no_simpleMethod<U>* dummy = nullptr) {
    std::cout << "Doing nothing... (don't have method or not constructible)";
    std::cout << std::endl;
  }

  template<typename U = T>
  void methodImpl(has_simpleMethod<U>* dummy = nullptr) {
    _t->simpleMethod(std::shared_ptr<SimpleFooImpl<U>>(make_shared()));
  }

  SimpleFooImplPtr<T> make_shared() {
    return std::static_pointer_cast<SimpleFooImpl<T>>(this->shared_from_this());
  }
};

/* CLASS CachedFooImpl ********************************************************/

// Forward declaration
template<typename T>
class CachedFooImpl;

// Alias
template<typename T>
using CachedFooImplPtr = std::shared_ptr<CachedFooImpl<T>>;

/**
 * @class CachedFooImpl
 * Cached implementation of Foo front-end
 */
template<typename T>
class CachedFooImpl
    : public std::conditional<!std::is_void<typename T::base>::value,
               CachedFooImpl<typename T::base>, FooImpl>::type {
 public:
  // Alias
  using TPtr = std::shared_ptr<T>;

  // Constructor
  CachedFooImpl(TPtr t = TPtr())
      : _t(std::move(t)) {
  }

  // Overriden methods
  void method() override {
    methodImpl();
  }

  // Concrete methods
  int cache() {
    return _cache;
  }

 private:
  // Instance variables
  TPtr _t;
  int _cache = 0;

  // Concrete methods
  template<typename U = T>
  void methodImpl(no_cachedMethod<U>* dummy = nullptr) {
    std::cout << "Doing nothing... (don't have method or not constructible)";
    std::cout << std::endl;
  }

  template<typename U = T>
  void methodImpl(has_cachedMethod<U>* dummy = nullptr) {
    _t->cachedMethod(std::shared_ptr<CachedFooImpl<U>>(make_shared()));
  }

  CachedFooImplPtr<T> make_shared() {
    return std::static_pointer_cast<CachedFooImpl<T>>(this->shared_from_this());
  }
};

/* CLASS Foo ******************************************************************/

// Forward declaration
class Foo;

// Alias
using FooPtr = std::shared_ptr<Foo>;

/**
 * @class Foo
 * Main class for Foo front-end
 */
class Foo {
 public:
  Foo(FooImplPtr impl) : _impl(std::move(impl)) {}

  virtual void method() {
    _impl->method();
  }

 private:
  FooImplPtr _impl;
};

/*
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
 -------------------------------------------------------------------------------
                               HIERARCHY BACK-END
 -------------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
*/

/* CLASS Top ******************************************************************/

// Forward declaration
class Top;

// Alias
using TopPtr = std::shared_ptr<Top>;

/**
 * @class Top
 * Top of the class hierarchy
 */
class Top : public std::enable_shared_from_this<Top> {
 public:
  using base = void;
};

/* CLASS Baz ******************************************************************/

// Forward declaration
class Baz;

// Alias
using BazPtr = std::shared_ptr<Baz>;

/**
 * @class Baz
 * Basic son of Top
 */
class Baz : public Top {
 public:
  using base = Top;
};

/* CLASS Bar ******************************************************************/

// Forward declaration
class Bar;

// Alias
using BarPtr = std::shared_ptr<Bar>;

/**
 * @class Bar
 * Complex son of Top, with new frontend
 */
class Bar : public Top {
 public:
  using base = Top;

  // Overriding methods
  virtual FooPtr simpleFoo() = 0;
  virtual FooPtr cachedFoo() = 0;

 protected:
  // Virtual methods
  virtual void simpleMethodImpl(SimpleFooImpl<Bar> *simpleFoo) {
    std::cout << "Running simple in Bar" << std::endl;
  }

  virtual void cachedMethodImpl(CachedFooImpl<Bar> *cachedFoo) {
    std::cout << "Running cached in Bar" << std::endl;
    cachedFoo->cache();
  }
};

/* CLASS BarDerived ***********************************************************/

// Forward declaration
class BarDerived;

// Alias
using BarDerivedPtr = std::shared_ptr<BarDerived>;

/**
 * @class BarDerived
 * Class "overriding" parent implementation
 */
class BarDerived : public Bar {
 public:
  using base = Bar;

  // Overriden methods
  FooPtr simpleFoo() override {
    return std::make_shared<Foo>(
      std::make_shared<SimpleFooImpl<BarDerived>>(make_shared()));
  }

  FooPtr cachedFoo() override {
    return std::make_shared<Foo>(
      std::make_shared<CachedFooImpl<BarDerived>>(make_shared()));
  }

  // Concrete methods
  template<typename... Params> // Required to implement `simpleFoo()`
  void simpleMethod(SimpleFooImplPtr<BarDerived> simpleFoo, Params... params) {
    std::cout << "BarDerived template method" << std::endl;
    simpleMethodImpl(simpleFoo.get(), std::forward<Params>(params)...);
  }

  template<typename... Params> // Required to implement `cachedFoo()`
  void cachedMethod(CachedFooImplPtr<BarDerived> cachedFoo, Params... params) {
    std::cout << "BarDerived template method" << std::endl;
    cachedMethodImpl(cachedFoo.get(), std::forward<Params>(params)...);
  }

 protected:
  // Virtual methods
  virtual void simpleMethodImpl(SimpleFooImpl<BarDerived> *simpleFoo) {
    std::cout << "Running simple in BarDerived" << std::endl;
  }

  virtual void cachedMethodImpl(CachedFooImpl<BarDerived> *cachedFoo) {
    std::cout << "Running cached in BarDerived" << std::endl;
    cachedFoo->cache();
  }

 private:
  BarDerivedPtr make_shared() {
    return std::static_pointer_cast<BarDerived>(this->shared_from_this());
  }
};

/* CLASS BarReusing ***********************************************************/

// Forward declaration
class BarReusing;

// Alias
using BarReusingPtr = std::shared_ptr<BarReusing>;

/**
 * @class BarReusing
 * Class reusing parent implementation
 */
class BarReusing : public Bar {
 public:
  using base = Bar;

  // Overriden methods
  FooPtr simpleFoo() override {
    return std::make_shared<Foo>(
      std::make_shared<SimpleFooImpl<BarReusing>>(make_shared()));
  }

  FooPtr cachedFoo() override {
    return std::make_shared<Foo>(
      std::make_shared<CachedFooImpl<BarReusing>>(make_shared()));
  }

  // Concrete methods
  template<typename... Params> // Required to implement `simpleFoo()`
  void simpleMethod(SimpleFooImplPtr<BarReusing> simpleFoo, Params... params) {
    std::cout << "BarReusing template method" << std::endl;
    simpleMethodImpl(simpleFoo.get(), std::forward<Params>(params)...);
  }

  template<typename... Params> // Required to implement `simpleFoo()`
  void cachedMethod(CachedFooImplPtr<BarReusing> cachedFoo, Params... params) {
    std::cout << "BarReusing template method" << std::endl;
    cachedMethodImpl(cachedFoo.get(), std::forward<Params>(params)...);
  }

 private:
  BarReusingPtr make_shared() {
    return std::static_pointer_cast<BarReusing>(this->shared_from_this());
  }
};

/*
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
 -------------------------------------------------------------------------------
                                      MAIN
 -------------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
*/

/* FUNCTION main **************************************************************/

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

  std::cout << std::endl;

  std::cout << "Test BarDerived" << std::endl;
  std::cout << "================" << std::endl;
  auto barDerived = std::make_shared<BarDerived>();
  barDerived->simpleFoo()->method();
  barDerived->cachedFoo()->method();

  std::cout << std::endl;

  std::cout << "Test BarDerived casted to Bar" << std::endl;
  std::cout << "==============================" << std::endl;
  static_cast<BarPtr>(barDerived)->simpleFoo()->method();
  static_cast<BarPtr>(barDerived)->cachedFoo()->method();

  std::cout << std::endl;

  std::cout << "Test BarReusing" << std::endl;
  std::cout << "================" << std::endl;
  auto barReusing = std::make_shared<BarReusing>();
  barReusing->simpleFoo()->method();
  barReusing->cachedFoo()->method();

  std::cout << std::endl;

  std::cout << "Test BarReusing casted to Bar" << std::endl;
  std::cout << "==============================" << std::endl;
  static_cast<BarPtr>(barReusing)->simpleFoo()->method();
  static_cast<BarPtr>(barReusing)->cachedFoo()->method();

  std::cout << std::endl;

  return 0;
}
igorbonadio commented 9 years ago

Man, it awesome! You are the best c++ programmer I've ever known. hahahahah

renatocf commented 9 years ago

Hahaha, thanks! But let me tell you: I found what I believe to be an even better solution - with much less strange variadic templates. Take a look:

// Standard headers
#include <memory>
#include <iostream>
#include <typeinfo>
#include <type_traits>

/*
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
 -------------------------------------------------------------------------------
                              IMPLEMENTATION HELPER
 -------------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
*/

/* MACRO GENERATE_HAS_MEMBER **************************************************/

// This macro is aimed to create a member detector: a class and type trait
// that can be used to check when an attribute/method exists in a class.
// As there is an infinite number of names, it's impossible to create a
// standard type trait only with the resources the language provides. Given
// that, it's necessary to create a macro to automatically generate all
// classes and alias from a name given as parameter.

// The following macro creates:
// - A template class that uses SFINAE and multiple inheritance to decide if
//   the member exists in the class. In case it does, the inner class `Derived`
//   has two `member`s and its size is the same is a char[2]. This information
//   is to store in `RESULT` a boolean that indicates a member exists.
// - A struct inheriting from `std::integral_constant`, which have a trait
//   compliant with STL.
// - Two alias `has_##member` and `no_##member` to selectively create
//   methods by applying SFINAE on its parameters.

#define GENERATE_HAS_MEMBER(member)                                         \
                                                                            \
template <typename T>                                                       \
class HasMember_##member                                                    \
{                                                                           \
private:                                                                    \
    using Yes = char[2];                                                    \
    using  No = char[1];                                                    \
                                                                            \
    struct Fallback { int member; };                                        \
    struct Derived : T, Fallback { };                                       \
                                                                            \
    template<typename U> static No&  test (decltype(U::member)*);           \
    template<typename U> static Yes& test (U*);                             \
                                                                            \
public:                                                                     \
    static constexpr bool RESULT                                            \
      = sizeof(test<Derived>(nullptr)) == sizeof(Yes);                      \
};                                                                          \
                                                                            \
template<typename T>                                                        \
struct has_member_##member                                                  \
    : public std::integral_constant<bool, HasMember_##member<T>::RESULT> {  \
};                                                                          \
                                                                            \
template<typename Model>                                                    \
using has_##member = typename                                               \
  std::enable_if<has_member_##member<Model>::value                          \
                 && std::is_constructible<Model>::value, bool>::type;       \
                                                                            \
template<typename Model>                                                    \
using no_##member = typename                                                \
  std::enable_if<!has_member_##member<Model>::value                         \
                 || !std::is_constructible<Model>::value, bool>::type;

// Generate the above structure for the following list of methods:
GENERATE_HAS_MEMBER(simpleMethod)
GENERATE_HAS_MEMBER(cachedMethod)

/*
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
 -------------------------------------------------------------------------------
                               HIERARCHY FRONT-END
 -------------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
*/

/* CLASS FooImpl **************************************************************/

// Forward declaration
class FooImpl;

// Alias
using FooImplPtr = std::shared_ptr<FooImpl>;

/**
 * @class FooImpl
 * Interface for implementation of Foo front-end
 */
class FooImpl : public std::enable_shared_from_this<FooImpl> {
 public:
  // Virtual methods
  virtual void method() = 0;
};

/* CLASS SimpleFooImpl ********************************************************/

// Forward declaration
template<typename T>
class SimpleFooImpl;

// Alias
template<typename T>
using SimpleFooImplPtr = std::shared_ptr<SimpleFooImpl<T>>;

/**
 * @class SimpleFooImpl
 * Simple implementation of Foo front-end
 */
template<typename T>
class SimpleFooImpl
    : public std::conditional<!std::is_void<typename T::base>::value,
               SimpleFooImpl<typename T::base>, FooImpl>::type {
 public:
  // Alias
  using TPtr = std::shared_ptr<T>;

  // Constructor
  SimpleFooImpl(TPtr t = TPtr())
      : _t(std::move(t)) {
  }

  // Overriden methods
  void method() override {
    methodImpl();
  }

 private:
  // Instance variables
  TPtr _t;

  // Concrete methods
  template<typename U = T>
  void methodImpl(no_simpleMethod<U>* dummy = nullptr) {
    std::cout << "Doing nothing... (don't have method or not constructible)";
    std::cout << std::endl;
  }

  template<typename U = T>
  void methodImpl(has_simpleMethod<U>* dummy = nullptr) {
    _t->simpleMethod(std::shared_ptr<SimpleFooImpl<U>>(make_shared()));
  }

  SimpleFooImplPtr<T> make_shared() {
    return std::static_pointer_cast<SimpleFooImpl<T>>(this->shared_from_this());
  }
};

/* CLASS CachedFooImpl ********************************************************/

// Forward declaration
template<typename T>
class CachedFooImpl;

// Alias
template<typename T>
using CachedFooImplPtr = std::shared_ptr<CachedFooImpl<T>>;

/**
 * @class CachedFooImpl
 * Cached implementation of Foo front-end
 */
template<typename T>
class CachedFooImpl
    : public std::conditional<!std::is_void<typename T::base>::value,
               CachedFooImpl<typename T::base>, FooImpl>::type {
 public:
  // Alias
  using TPtr = std::shared_ptr<T>;
  using Cache = typename T::Cache;

  // Constructor
  CachedFooImpl(TPtr t = TPtr(), Cache cache = Cache())
      : _t(std::move(t)), _cache(std::move(cache)) {
  }

  // Overriden methods
  void method() override {
    methodImpl();
  }

  // Concrete methods
  Cache cache() {
    return _cache;
  }

 private:
  // Instance variables
  TPtr _t;
  Cache _cache;

  // Concrete methods
  template<typename U = T>
  void methodImpl(no_cachedMethod<U>* dummy = nullptr) {
    std::cout << "Doing nothing... (don't have method or not constructible)";
    std::cout << std::endl;
  }

  template<typename U = T>
  void methodImpl(has_cachedMethod<U>* dummy = nullptr) {
    _t->cachedMethod(std::shared_ptr<CachedFooImpl<U>>(make_shared()));
  }

  CachedFooImplPtr<T> make_shared() {
    return std::static_pointer_cast<CachedFooImpl<T>>(this->shared_from_this());
  }
};

/* CLASS Foo ******************************************************************/

// Forward declaration
class Foo;

// Alias
using FooPtr = std::shared_ptr<Foo>;

/**
 * @class Foo
 * Main class for Foo front-end
 */
class Foo {
 public:
  Foo(FooImplPtr impl) : _impl(std::move(impl)) {}

  virtual void method() {
    _impl->method();
  }

 private:
  FooImplPtr _impl;
};

/*
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
 -------------------------------------------------------------------------------
                               HIERARCHY BACK-END
 -------------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
*/

/* CLASS Top ******************************************************************/

// Forward declaration
class Top;

// Alias
using TopPtr = std::shared_ptr<Top>;

/**
 * @class Top
 * Top of the class hierarchy
 */
class Top : public std::enable_shared_from_this<Top> {
 public:
  using base = void;
  using Cache = int;
};

/* CLASS Baz ******************************************************************/

// Forward declaration
class Baz;

// Alias
using BazPtr = std::shared_ptr<Baz>;

/**
 * @class Baz
 * Basic son of Top
 */
class Baz : public Top {
 public:
  using base = Top;
};

/* CLASS Bar ******************************************************************/

// Forward declaration
class Bar;

// Alias
using BarPtr = std::shared_ptr<Bar>;

/**
 * @class Bar
 * Complex son of Top, with definition of new front-end
 */
class Bar : public Top {
 public:
  using base = Top;

  // Purely virtual methods
  virtual FooPtr simpleFoo() = 0;
  virtual FooPtr cachedFoo() = 0;

  // Virtual methods
  virtual void simpleMethod(SimpleFooImplPtr<Bar> simpleFoo) {
    std::cout << "Running simple in Bar" << std::endl;
  }

  virtual void cachedMethod(CachedFooImplPtr<Bar> cachedFoo) {
    std::cout << "Running cached in Bar" << std::endl;
    std::cout << "Cache: " << typeid(cachedFoo->cache()).name() << std::endl;
  }
};

/* CLASS BarCrtp **************************************************************/

// Forward declaration
template<typename Derived>
class BarCrtp;

// Alias
template<typename Derived>
using BarCrtpPtr = std::shared_ptr<BarCrtp<Derived>>;

/**
 * @class BarCrtp
 * Implementation of front-end, using CRTP to inject methods in subclasses
 */
template<typename Derived>
class BarCrtp : public Bar {
 public:
  using base = Bar;
  using DerivedPtr = std::shared_ptr<Derived>;

  // Overriding methods
  FooPtr simpleFoo() override {
    return std::make_shared<Foo>(
      std::make_shared<SimpleFooImpl<Derived>>(make_shared()));
  }

  FooPtr cachedFoo() override {
    return std::make_shared<Foo>(
      std::make_shared<CachedFooImpl<Derived>>(make_shared()));
  }

 private:
  DerivedPtr make_shared() {
    return std::static_pointer_cast<Derived>(
      static_cast<Derived *>(this)->shared_from_this());
  }
};

/* CLASS BarDerived ***********************************************************/

// Forward declaration
class BarDerived;

// Alias
using BarDerivedPtr = std::shared_ptr<BarDerived>;

/**
 * @class BarDerived
 * Class "overriding" parent implementation
 */
class BarDerived : public BarCrtp<BarDerived> {
 public:
  using base = Bar;
  using Cache = double;

  virtual void simpleMethod(SimpleFooImplPtr<BarDerived> simpleFoo) {
    std::cout << "Running simple in BarDerived" << std::endl;
  }

  virtual void cachedMethod(CachedFooImplPtr<BarDerived> cachedFoo) {
    std::cout << "Running cached in BarDerived" << std::endl;
    std::cout << "Cache: " << typeid(cachedFoo->cache()).name() << std::endl;
  }
};

/* CLASS BarReusing ***********************************************************/

// Forward declaration
class BarReusing;

// Alias
using BarReusingPtr = std::shared_ptr<BarReusing>;

/**
 * @class BarReusing
 * Class reusing parent implementation
 */
class BarReusing : public BarCrtp<BarReusing> {
 public:
  using base = Bar;
};

/*
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
 -------------------------------------------------------------------------------
                                      MAIN
 -------------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
*/

/* FUNCTION main **************************************************************/

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

  std::cout << std::endl;

  std::cout << "Test BarDerived" << std::endl;
  std::cout << "================" << std::endl;
  auto barDerived = std::make_shared<BarDerived>();
  barDerived->simpleFoo()->method();
  barDerived->cachedFoo()->method();

  std::cout << std::endl;

  std::cout << "Test BarDerived casted to Bar" << std::endl;
  std::cout << "==============================" << std::endl;
  static_cast<BarPtr>(barDerived)->simpleFoo()->method();
  static_cast<BarPtr>(barDerived)->cachedFoo()->method();

  std::cout << std::endl;

  std::cout << "Test BarReusing" << std::endl;
  std::cout << "================" << std::endl;
  auto barReusing = std::make_shared<BarReusing>();
  barReusing->simpleFoo()->method();
  barReusing->cachedFoo()->method();

  std::cout << std::endl;

  std::cout << "Test BarReusing casted to Bar" << std::endl;
  std::cout << "==============================" << std::endl;
  static_cast<BarPtr>(barReusing)->simpleFoo()->method();
  static_cast<BarPtr>(barReusing)->cachedFoo()->method();

  std::cout << std::endl;

  return 0;
}

I changed two things:

renatocf commented 9 years ago

Well, I think this one will be my final implementation for the reformulated architecture.

Here, method in Foo could be applied for two different types: Target and Spot. Check the implementation:

// Standard headers
#include <memory>
#include <iostream>
#include <typeinfo>
#include <exception>
#include <type_traits>

/*
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
 -------------------------------------------------------------------------------
                              IMPLEMENTATION HELPER
 -------------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
*/

#define GENERATE_HAS_MEMBER(member)                                         \
                                                                            \
template <typename T>                                                       \
class HasMember_##member                                                    \
{                                                                           \
private:                                                                    \
    using Yes = char[2];                                                    \
    using  No = char[1];                                                    \
                                                                            \
    struct Fallback { int member; };                                        \
    struct Derived : T, Fallback { };                                       \
                                                                            \
    template<typename U> static No&  test (decltype(U::member)*);           \
    template<typename U> static Yes& test (U*);                             \
                                                                            \
public:                                                                     \
    static constexpr bool RESULT                                            \
      = sizeof(test<Derived>(nullptr)) == sizeof(Yes);                      \
};                                                                          \
                                                                            \
struct no_##member##_tag {};                                                \
struct has_##member##_tag {};                                               \
                                                                            \
template<typename T>                                                        \
struct has_member_##member                                                  \
    : public std::integral_constant<bool, HasMember_##member<T>::RESULT> {  \
                                                                            \
  using tag = typename std::conditional<has_member_##member<T>::value,      \
                has_##member##_tag, no_##member##_tag>::type;               \
};

// Generate the above structure for the following list of methods:
GENERATE_HAS_MEMBER(simpleMethod)
GENERATE_HAS_MEMBER(cachedMethod)

/*
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
 -------------------------------------------------------------------------------
                                 COMMON CLASSES
 -------------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
*/

/* CLASS Target ***************************************************************/

class Target {
};

/* CLASS Spot *****************************************************************/

class Spot {
};

/*
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
 -------------------------------------------------------------------------------
                               HIERARCHY FRONT-END
 -------------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
*/

/* CLASS Foo ******************************************************************/

// Forward declaration
template<typename T>
class Foo;

// Alias
template<typename T>
using FooPtr = std::shared_ptr<Foo<T>>;

/**
 * @class Foo
 * Interface for implementation of Foo front-end
 */
template<typename T>
class Foo : public std::enable_shared_from_this<Foo<T>> {
 public:
  // Virtual methods
  virtual void method() = 0;
};

/* CLASS SimpleFoo ************************************************************/

// Forward declaration
template<typename T, typename M>
class SimpleFoo;

// Alias
template<typename T, typename M>
using SimpleFooPtr = std::shared_ptr<SimpleFoo<T, M>>;

/**
 * @class SimpleFoo
 * Simple implementation of Foo front-end
 */
template<typename T, typename M>
class SimpleFoo
    : public std::conditional<!std::is_void<typename M::base>::value,
               SimpleFoo<T, typename M::base>, Foo<T>>::type {
 public:
  // Alias
  using MPtr = std::shared_ptr<M>;

  // Constructor
  SimpleFoo(MPtr m = MPtr())
      : _m(std::move(m)) {
  }

 public:

  // Overriden methods
  void method() override {
    methodImpl(typename has_member_simpleMethod<M>::tag());
  }

 private:
  // Instance variables
  MPtr _m;

  // Concrete methods
  void methodImpl(no_simpleMethod_tag) {
    throw std::logic_error("Class don't have method!");
  }

  void methodImpl(has_simpleMethod_tag) {
    _m->simpleMethod(make_shared());
  }

  SimpleFooPtr<T, M> make_shared() {
    return std::static_pointer_cast<SimpleFoo<T, M>>(
      this->shared_from_this());
  }
};

/* CLASS CachedFoo ************************************************************/

// Forward declaration
template<typename T, typename M>
class CachedFoo;

// Alias
template<typename T, typename M>
using CachedFooPtr = std::shared_ptr<CachedFoo<T, M>>;

/**
 * @class CachedFoo
 * Cached implementation of Foo front-end
 */
template<typename T, typename M>
class CachedFoo
    : public std::conditional<!std::is_void<typename M::base>::value,
               CachedFoo<T, typename M::base>, Foo<T>>::type {
 public:
  // Alias
  using MPtr = std::shared_ptr<M>;
  using Cache = typename M::Cache;

  // Constructor
  CachedFoo(MPtr m = MPtr(), Cache cache = Cache())
      : _m(std::move(m)), _cache(std::move(cache)) {
  }

  // Overriden methods
  void method() override {
    methodImpl(typename has_member_cachedMethod<M>::tag());
  }

  // Concrete methods
  Cache cache() {
    return _cache;
  }

 private:
  // Instance variables
  MPtr _m;
  Cache _cache;

  // Concrete methods
  void methodImpl(no_cachedMethod_tag) {
    throw std::logic_error("Class don't have method!");
  }

  void methodImpl(has_cachedMethod_tag) {
    _m->cachedMethod(make_shared());
  }

  CachedFooPtr<T, M> make_shared() {
    return std::static_pointer_cast<CachedFoo<T, M>>(
      this->shared_from_this());
  }
};

/*
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
 -------------------------------------------------------------------------------
                               HIERARCHY BACK-END
 -------------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
*/

/* CLASS Top ******************************************************************/

// Forward declaration
class Top;

// Alias
using TopPtr = std::shared_ptr<Top>;

/**
 * @class Top
 * Top of the class hierarchy
 */
class Top : public std::enable_shared_from_this<Top> {
 public:
  using base = void;
  using Cache = int;
};

/* CLASS Baz ******************************************************************/

// Forward declaration
class Baz;

// Alias
using BazPtr = std::shared_ptr<Baz>;

/**
 * @class Baz
 * Basic son of Top
 */
class Baz : public Top {
 public:
  using base = Top;
};

/* CLASS Bar ******************************************************************/

// Forward declaration
class Bar;

// Alias
using BarPtr = std::shared_ptr<Bar>;

/**
 * @class Bar
 * Complex son of Top, with definition of new front-end
 */
class Bar : public Top {
 public:
  using base = Top;

  // Purely virtual methods
  virtual FooPtr<Target> targetFoo(bool cached) = 0;
  virtual FooPtr<Spot> spotFoo(bool cached) = 0;
};

/* CLASS BarCrtp **************************************************************/

// Forward declaration
template<typename Derived>
class BarCrtp;

// Alias
template<typename Derived>
using BarCrtpPtr = std::shared_ptr<BarCrtp<Derived>>;

/**
 * @class BarCrtp
 * Implementation of front-end, using CRTP to inject methods in subclasses
 */
template<typename Derived>
class BarCrtp : public Bar {
 public:
  using base = Bar;
  using DerivedPtr = std::shared_ptr<Derived>;

  // Overriding methods
  FooPtr<Target> targetFoo(bool cached = true) override {
    if (cached)
      return std::make_shared<CachedFoo<Target, Derived>>(make_shared());
    return std::make_shared<SimpleFoo<Target, Derived>>(make_shared());
  }

  FooPtr<Spot> spotFoo(bool cached = true) override {
    if (cached)
      return std::make_shared<CachedFoo<Spot, Derived>>(make_shared());
    return std::make_shared<SimpleFoo<Spot, Derived>>(make_shared());
  }

  // Virtual methods
  virtual void simpleMethod(SimpleFooPtr<Target, Derived> simpleFoo) {
    std::cout << "Running simple for Target in BarCrtp" << std::endl;
  }

  virtual void cachedMethod(CachedFooPtr<Target, Derived> cachedFoo) {
    std::cout << "Running cached for Target in BarCrtp" << std::endl;
    std::cout << "Cache: " << typeid(cachedFoo->cache()).name() << std::endl;
  }

  virtual void simpleMethod(SimpleFooPtr<Spot, Derived> simpleFoo) {
    std::cout << "Running simple for Spot in BarCrtp" << std::endl;
  }

  virtual void cachedMethod(CachedFooPtr<Spot, Derived> cachedFoo) {
    std::cout << "Running cached for Spot in BarCrtp" << std::endl;
    std::cout << "Cache: " << typeid(cachedFoo->cache()).name() << std::endl;
  }

 private:
  DerivedPtr make_shared() {
    return std::static_pointer_cast<Derived>(
      static_cast<Derived *>(this)->shared_from_this());
  }
};

/* CLASS BarDerived ***********************************************************/

// Forward declaration
class BarDerived;

// Alias
using BarDerivedPtr = std::shared_ptr<BarDerived>;

/**
 * @class BarDerived
 * Class "overriding" parent implementation
 */
class BarDerived : public BarCrtp<BarDerived> {
 public:
  using base = Bar;
  using Cache = double;

  // Overriden methods
  void simpleMethod(SimpleFooPtr<Target, BarDerived> simpleFoo) override {
    std::cout << "Running simple for Target in BarDerived" << std::endl;
  }

  void cachedMethod(CachedFooPtr<Target, BarDerived> cachedFoo) override {
    std::cout << "Running cached for Target in BarDerived" << std::endl;
    std::cout << "Cache: " << typeid(cachedFoo->cache()).name() << std::endl;
  }

  void simpleMethod(SimpleFooPtr<Spot, BarDerived> simpleFoo) override {
    std::cout << "Running simple for Spot in BarDerived" << std::endl;
  }

  void cachedMethod(CachedFooPtr<Spot, BarDerived> cachedFoo) override {
    std::cout << "Running cached for Spot in BarDerived" << std::endl;
    std::cout << "Cache: " << typeid(cachedFoo->cache()).name() << std::endl;
  }
};

/* CLASS BarReusing ***********************************************************/

// Forward declaration
class BarReusing;

// Alias
using BarReusingPtr = std::shared_ptr<BarReusing>;

/**
 * @class BarReusing
 * Class reusing parent implementation
 */
class BarReusing : public BarCrtp<BarReusing> {
 public:
  using base = Bar;
};

/*
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
 -------------------------------------------------------------------------------
                                      MAIN
 -------------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
*/

/* FUNCTION main **************************************************************/

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

  std::cout << std::endl;

  std::cout << "Test BarDerived" << std::endl;
  std::cout << "================" << std::endl;
  auto barDerived = std::make_shared<BarDerived>();
  barDerived->targetFoo(false)->method();
  barDerived->targetFoo(true)->method();
  barDerived->spotFoo(false)->method();
  barDerived->spotFoo(true)->method();

  std::cout << std::endl;

  std::cout << "Test BarDerived casted to Bar" << std::endl;
  std::cout << "==============================" << std::endl;
  static_cast<BarPtr>(barDerived)->targetFoo(false)->method();
  static_cast<BarPtr>(barDerived)->targetFoo(true)->method();
  static_cast<BarPtr>(barDerived)->spotFoo(false)->method();
  static_cast<BarPtr>(barDerived)->spotFoo(true)->method();

  std::cout << std::endl;

  std::cout << "Test BarReusing" << std::endl;
  std::cout << "================" << std::endl;
  auto barReusing = std::make_shared<BarReusing>();
  barReusing->targetFoo(false)->method();
  barReusing->targetFoo(true)->method();
  barReusing->spotFoo(false)->method();
  barReusing->spotFoo(true)->method();

  std::cout << std::endl;

  std::cout << "Test BarReusing casted to Bar" << std::endl;
  std::cout << "==============================" << std::endl;
  static_cast<BarPtr>(barReusing)->targetFoo(false)->method();
  static_cast<BarPtr>(barReusing)->targetFoo(true)->method();
  static_cast<BarPtr>(barReusing)->spotFoo(false)->method();
  static_cast<BarPtr>(barReusing)->spotFoo(true)->method();

  std::cout << std::endl;

  return 0;
}

So far, this is the most simple (and I think the best) version:

renatocf commented 9 years ago

New TODO list to change Generator's parameter from target to decorator:

igorbonadio commented 9 years ago

We need to include a number generator to the generator frontend