Boost.TypeErasure — Counter

#include <cassert>
#include <iostream>
#include <boost/type_erasure/any.hpp>
#include <boost/type_erasure/operators.hpp> // decrementable
#include <boost/concept_check.hpp>
#include <boost/mpl/vector.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) Mini-concept for contextual conversion to bool

template <class T>
struct testable
{
    static bool apply(const T& arg) { return bool(arg); }
};

// (3) Teaching framework the conversion to bool

namespace boost { namespace type_erasure {

  template<class T, class Base>
  struct concept_interface <testable<T>, Base, T> : Base
  {
    explicit operator bool () const
    { return call(testable<T>(), *this); }
  };

}}

using boost::type_erasure::decrementable;
namespace te = boost::type_erasure;

// (4) Composing the interface functions

using Counter = boost::mpl::vector<
  te::copy_constructible<>, 
  decrementable<>, 
  testable<te::_self>, 
  te::relaxed
>;

// (5) The type-erased interface

using AnyCounter = te::any<Counter>;

// (6) Custom Counter for tests

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 = LoggingCounter{};
                               // reset with a different type
  assert (counter1);           // 2
  --counter1;
  assert (counter1);           // 1
  --counter1;
  assert (!counter1);          // 0
}

int main ()
{
  test_counter();
}

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.