xany: Creating C++ Generalized adaptors

Copyright (C) 2012 Arman Hunanyan

Contents

  1. Overview
  2. Quick Start

Overview

There are a few numbers of Generalized Function Adaptors available for C++ developers (boost::any, boost::function, any_iterator ...). The problem is that all adaptors are designed for specific suite of types. If we need a different type of adaptor we have to write it from the scratch. For example if we need to keep all application options in container we can use boost::any . But if we need to save this container into text file we need our adaptor to have to_string method which is not offered by boost::any. So we need to implement string_convertable_any by duplicating implementation of boost::any and adding one small function to_string. xany is intended to solve this problem. xany is template class. Usually the interface of template class doesn't depend from template parameter. For example std::list for any type has predefined set of members. The set of member functions of xany depends from T. As C++ doesn't allow defining member functions in compile time, additional member function need to be defined inside some empty class and pass this class to xany. This makes instansietion of xany a little bit difficult. But anyway using xany is easier than implementing adaptors from the scratch.

Quick Start

xany takes Boost Mpl Sequence of method definitions as template argument. When instantiating with empty sequence xany is similar to boost::any. xany_cast extracts a value from xany object.

Example1: Generating string_convertable_any

Let's define to_string and from_string methods and pass it to xany. Method definition for xany is a template class which should provide three nested types: signature, wrapper and implementation.


template <typename Xany>
struct to_string_method
{
    typedef std::string (Xany::* signature) () const;

    template <typename T>
    struct wrapper
        : public T
    {
        std::string to_string() const
        {
            return this->call(to_string_method());
        }
    };

    struct implementation
    {
        template <typename T>
        std::string operator() (const T& t) const
        {
            std::ostringstream oss;
            oss << t;
            return oss.str();
        }
    };
};

First type defines the signature of method. wrapper template class defines the method itself and implement it by calling call method. Actually the generated class will be derived from wrapper and to_string will become a member function of it. Third type is a functional object which must operate with containing value (convert value to std::string in this case). As a result implementation adds additional requirement for types supported by adaptor. The ValueType will be supported by result adaptor if

  1. Satisfies boost::any's type requirements. See Boost.Any  

  2. All methods implementations are successfully compiling with ValueType argument. 


template <typename Xany>
struct from_string_method
{
    typedef void (Xany::* signature) (const std::string&);

    template <typename T>
    struct wrapper
        : public T
    {
        void from_string(const std::string& str)
        {
            this->call(from_string_method(), str);
        }
    };

    struct implementation {
        template <typename T>
        void operator() (T& t, const std::string& str) const
        {
            std::istringstream iss(str);
            iss >> t;
        }
    };
};

Now we can generate required type by passing these definitions to xany


typedef xany<boost::mpl::list<to_string_method<boost::mpl::_1>, from_string_method<boost::mpl::_1> > > string_convertable_any;

After instansietion boost::mpl::_1 will be replaced by xany. string_convertable_any provides to_string and from_string additional members. The string_convertable_any will accept any type that satisfies ValueType requirements of boost::any and for which stream insertion and extraction operations are defined.


	string_convertable_any a(10);
	std::string str = a.to_string();
	a.from_string("100");

Example2:Generaing any_function

Let's generate simple generalized function adaptor using xany. For simplicity our any_function will support up to two arguments.


template <typename FunctionSignature, typename Xany>
struct call_method;

template <typename R, typename Xany>
struct call_method<R (), Xany>
{
    typedef R (Xany::* signature) ();
    template <typename T>
    struct wrapper
        : public T
    {
        R operator() ()
        {
            return this->call(call_method());
        }
    };
    struct implementation
    {
        template <typename T>
        R operator() (T& t) const
        {
            return t();
        }
    };
};

template <typename R, typename T1, typename Xany>
struct call_method<R (T1), Xany>
{
    typedef R (Xany::* signature) (T1);
    template <typename T>
    struct wrapper
        : public T
    {
        R operator() (T1 a1)
        {
            return this->call(call_method(), a1);
        }
    };
    struct implementation
    {
        template <typename T>
        R operator() (T& t, T1 a1) const
        {
            return t(a1);
        }
    };
};

template <typename R, typename T1, typename T2, typename Xany>
struct call_method<R (T1, T2), Xany>
{
    typedef R (Xany::* signature) (T1, T2);
    template <typename T>
    struct wrapper
        : public T
    {
        R operator() (T1 a1, T2 a2)
        {
            return this->call(call_method(), a1, a2);
        }
    };
    struct implementation
    {
        template <typename T>
        R operator() (T& t, T1 a1, T2 a2) const
        {
            return t(a1, a2);
        }
    };
};


We can define any_function as Boost MPLs Higher-Order Metafunction.


struct any_function_f
{
    template <typename Signature>
    struct apply
    {
        typedef xany<boost::mpl::list<call_method<Signature, boost::mpl::_1> > > type;
    };
};


boost::mpl::apply<any_function_f, Signature> will be generalized function adaptor for Signature.

Example3:Generaing comparable_any

Let's generate generalized adaptor for equality comparable types


template <typename Xany>
struct equal_method
{
    typedef bool (Xany::* signature) (const Xany&) const;

    template <typename T>
    struct wrapper
        : public T
    {
        bool operator== (const Xany& other) const
        {
            return this->call(equal_method(), other);
        }
    };

    struct implementation
    {
        template <typename T>
        bool operator() (const T& t, const Xany& other) const
        {
            return t == xany_cast<T>(other);
        }
    };
};

template <typename Xany>
struct nequal_method
{
    typedef bool (Xany::* signature) (const Xany&) const;

    template <typename T>
    struct wrapper
        : public T
    {
        bool operator!= (const Xany& other) const
        {
            return this->call(nequal_method(), other);
        }
    };

    struct implementation
    {
        template <typename T>
        bool operator() (const T& t, const Xany& other) const
        {
            return t != xany_cast<T>(other);
        }
    };
};

typedef xany<boost::mpl::list<equal_method<boost::mpl::_1>, nequal_method<boost::mpl::_1> > > comparable_any;

    comparable_any a1(10);
    comparable_any a2(12);
    comparable_any a3(10);
    assert(! (a1 == a2) );
    assert(a1 == a3);
    assert(a1 != a2);