How I Boost.Any was friends with Boost.MPL

Recently, I had to work with code in which the task of passing parameters of arbitrary types was solved using standard STL containers parameterized with boost :: any type.
For instance:
    void foo (std::vector& args) {
        // do smth.
    }

The previous developer was not very accurate and inside the function, working with the contents of boost :: any was based on the assumption of the original data type, that is, if the conversion of boost :: any_cast did not pass, then the parameter was skipped. In certain cases, this processing method is acceptable and examples of this working technique can be found here .
However, I wanted to generalize some initial assumptions about the data type.

Imagine a situation where std :: vector contains data of the following types:
short, int, float, double, and there is a function
    double sum (std::vector& args) {
    }

which calculates the sum of the values ​​of the passed parameters, calling boost :: any_castfor each element of the container.
Let's see what happens when boost :: any_cast is converted
    template
    ValueType * any_cast(any * operand)
    {
            // 
            // some code skipped
            //
            operand->type() == typeid(ValueType)
            ? &static_cast *>(operand->content)->held
            : 0;
    }

If the values ​​of type_info of the original type and the type to which the conversion is performed do not match, then returns 0. But, returning to the sum function, in the usual situation, we are quite able to write ourselves
    double value=1+2L+3.0F+4.0;

thus adding up int, long, float and double. We want to achieve the same behavior from the sum function.

That is, when processing the boost :: any value, we want to say: “Hey, the function 'Smth cast (any)', if you can, convert any to type Smth.” And in order for the cast function to know what types it can lead to Smth, we need lists of types. Essentially, we want to specify a list of types that can be easily converted to the required type.
To describe the list of convertible types, we will not reinvent the wheel, but take the boost :: mpl library and find the concept of type sequences we need there .

Now we need the type converter class as a substitute for boost :: any_cast, as well as the type converter class, which
  1. Accepts the type we want to convert ToType to
  2. Accepts a list of CompatibleTypes types from which conversion to the required ToType type is possible
  3. Based on the boost :: any object, it would return us a suitable type converter.

The converter class must provide us with a function to convert to the required type, and also store a pointer to a type_info object of the type from which it can perform the conversion:
 template
 class Converter {
 public:
  virtual ~Converter {}
  virtual ToType cast(const boost::any& arg) = 0;
  explicit Converter(const type_info* info) : m_info(info) { }
  const type_info& type() {
   return *m_info;
  }
 private:
  const type_info* m_info;
 };

Why the cast function is declared purely virtual will become clear a little lower. Now we describe the class dispatcher:
    template
    class TypesDispatcher {
        std::vector* > converters;
        struct wrapper {
        //code skipped
        };
    public:
        TypesDispatcher();
        Converter* getConverter(const boost::any& arg);
    };

The dispatcher class is parameterized by a sequence of valid CompatibleTypes types and the type to which the ToType conversion is performed. The container holds a container of pointers to Converter. We need each Converter to be bound to one of the types listed in CompatibleTypes. To do this, we will inherit from Converter and implement the cast function:
    template 
    class TypeConverter : public Converter {
    public:
        TypeConverter() : Converter(&typeid(FromType)) { }
        virtual ToType cast(const boost::any& arg) {
            return static_cast(*boost::any_cast(&arg));
        }
    };

Now we have everything to create converter objects for all types in CompatibleTypes, as well as implement the getConverter function.
    // функтор обертка над контейнером преобразователей
    struct wrapper {
        explicit wrapper(std::vector* >& converters) 
                    : m_converters(converters)
        {
        }
        template
        void operator()(FromType) {
            m_converters.push_back(new TypeConverter());
        }
        std::vector* >& m_converters;
    };
    //конструктор класса диспетчера типов
    TypesDispatcher() {
        boost::mpl::for_each(wrapper(converters));
    }

To go through the list of types of CompatibleTypes we used boost :: mpl :: for_each, which takes an object the function applied to each type in the list at runtime, for this instantiating an object of each type.
The implementation of the getConverter function is simple and obvious:
    Converter* getConverter(const boost::any& arg) {
        std::vector* >::const_iterator it = converters.begin();
        while(it != converters.end() ) {
            Converter* converter = *it;
            if(converter->type() == arg.type())
                return converter;
            ++it;
        }
        return 0;
    }

That's all. Now we can write something like
    template
    ReturnType sum(std::vector& args) {
        TypesDispatcher dispatcher;
        std::vector::size_type i;
        ReturnType result = ReturnType();
        for(i=0; i < args.size(); i++) {
            Converter* converter = dispatcher.getConverter(args[i]);
            if(converter)
                result += converter->cast(args[i]);
        }
        return result;
    }
    int main(int argc, char* argv[]) {
        typedef boost::mpl::vector types;
        std::vector seq;
        seq.push_back(boost::any( 1 ));
        seq.push_back(boost::any( 2L ));
        seq.push_back(boost::any( 3.0F ));
        seq.push_back(boost::any( 4.0 ));
        double result = sum(seq); 
    }


The complete code example can be found here .

As a result, we got the opportunity to work with arguments of arbitrary types, relatively flexible setting the rules for their conversion.

Also popular now: