Adobe.Poly — Counter

#include <cassert>
#include <iostream>
#include <adobe/poly.hpp>
#include <boost/concept_check.hpp>

// (1) Concept for compile-time tests

template <typename C>
struct CountdownCounter : boost::Assignable<C>
                        , boost::CopyConstructible<C>
{
  BOOST_CONCEPT_USAGE(CountdownCounter)
  {
    --c;
    (bool(c));
    bool b = !c;
    (bool(--c));
  }
  
private:
    C c;
};

// (2) The inner interface

struct CounterIface : adobe::poly_copyable_interface
{
  virtual void decrement() = 0;
  virtual bool done() const = 0;
};

// (3) The inner interface implementation

template <typename T>
struct CounterImpl 
  : adobe::optimized_storage_type<T, CounterIface>::type
{
    using base_t = typename 
    adobe::optimized_storage_type<T, CounterIface>::type;

  BOOST_CONCEPT_ASSERT((CountdownCounter<T>));
  
  CounterImpl(T x) : base_t(x) {}
  CounterImpl(adobe::move_from<CounterImpl> x) 
    : base_t(adobe::move_from<base_t>(x.source)) {}
  
  void decrement() override { --this->get(); }
  bool done() const override { return bool(this->get()); }
};

// (4) The outer interface specification

struct Counter : adobe::poly_base<CounterIface, CounterImpl>
{
  using base_t = adobe::poly_base<CounterIface, CounterImpl>;
  using base_t::base_t; // Inherit constructors

  Counter(adobe::move_from<Counter> x) 
    : base_t(adobe::move_from<base_t>(x.source)) {}
  
  Counter& operator--() 
  { 
    interface_ref().decrement(); 
    return *this; 
  }
  
  explicit operator bool() const 
  { 
    return interface_ref().done(); 
  }
};

// (5) The interface

typedef adobe::poly<Counter> AnyCounter;

// (6) Another counter for testing

struct LoggingCounter
{
  int c = 2;
  explicit operator bool () const { return c; }
  
  LoggingCounter& operator--() 
  { 
    std::cout << "decremented\n";
    --c;
    return *this;
  }
};
 
// (7) Compile-time test

BOOST_CONCEPT_ASSERT((CountdownCounter<int>)); 
BOOST_CONCEPT_ASSERT((CountdownCounter<LoggingCounter>)); 
BOOST_CONCEPT_ASSERT((CountdownCounter<AnyCounter>));

// (8) Run-time test

void  test_counter()
{
  AnyCounter counter1 {2};  // bind to int (initially 2)
  assert (counter1);        // still counting
  assert (--counter1);      // still counting (1)
  AnyCounter counter2 = counter1;
                            // counter2 (int) counts from 1
  --counter1;
  assert (!counter1);       // done
  assert (counter2);        // counter2 still 1
  assert (!--counter2);     // counter2 also done
     
  counter1 = AnyCounter{LoggingCounter{}};
                            // reset with a different type
  assert (counter1);        // 2
  --counter1;
  assert (counter1);        // 1
  --counter1;
  assert (!counter1);       // 0
}

int main()
{
  test_counter();
}

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s