Search code examples
c++boostforeachreturnboost-mpl

How to use Boost MPL to have several return points in a function?


Consider the following code:

struct Param {};
struct Base {
        virtual char f(Param const &) const = 0;
};
struct A : Base {
        explicit A(Param const &) {}
        virtual char f(Param const &) const {return 'A';}
};
struct B : Base {
        explicit B(Param const &) {}
        virtual char f(Param const &) const {return 'B';}
};
struct C : Base {
        explicit C(Param const &) {}
        virtual char f(Param const &) const {return 'C';}
};

char my_function(Param const & param, Base const & base)
{
        if(A const * const p = dynamic_cast<A const *>(&base)) {
                return p->f(param);
        }
        if(B const * const p = dynamic_cast<B const *>(&base)) {
                return p->f(param);
        }
        if(C const * const p = dynamic_cast<C const *>(&base)) {
                return p->f(param);
        }
        return '0';
}

Then if I write

Param x, y;
std::cout << my_function(x, B(y)) << std::endl;

it outputs B.

My goal is to change the implementation of my_function so that it can support a set of subtypes of Base defined at compile-time. Here I have hand-written the expanded code I would obtain for the set of types {A, B, C}. I would like to templatize my_function with a set of types, and call it this way:

std::cout << my_function<boost::mpl::set<A, B, C> >(x, B(y)) << std::endl;

outputting B, or, for instance:

std::cout << my_function<boost::mpl::set<A, C> >(x, B(y)) << std::endl;

outputting 0.

I don't know which MPL construct to use to achieve this result.

I initially thought a call to find_if could allow to find the first type in the set to which base can be dynamic_casted, but in fact this algorithm, like most MPL algorithms, produces its result statically so it obviously can't be used with a runtime parameter (base).

The only MPL algorithm that is categorized "runtime" is for_each, but I don't manage to figure out how to use it to produce the same effect as my multiple return statements (and I even don't know whether it is possible). Thanks to any MPL-speaker who could help me.

PS: Don't tell me I should avoid dynamic casts or I could simply have

char my_function(Param const & param, Base const & base) {return base.f(param);}

I have simplified the code sample compared with my real-life problem and I cannot change the existing design.


Solution

  • Something like this should work :

    struct functor
    {
      public:
      functor(Param const & param, Base const & base)
      : param_(param),
        base_(base),
        found_(false)
      {
      }
    
      template<typename Child>
      void operator()(Child*)
      {
        if (!found_)
        {
          if(Child const * const p = dynamic_cast<Child const *>(&base_))
          {
            value_ = p->f(param_);
            found_ = true;
          }
        }
      }
    
      char get_value()
      {
         return value_;
      }
    
      private:
      Param const & param_;
      Base const & base_;
      bool found_;
      char value_;
    };
    
    template <typename ...Types>
    char my_function(Param const & param, Base const & base)
    {
      typedef boost::mpl::vector<Types...>::type children;
    
      functor f(param, base);
    
      boost::mpl::for_each<children>(std::ref(f));
    
      return f.get_value();
    }
    
    int main()
    {
      Param x, y;
      std::cout << my_function<A*, B*, C*>(x, B(y)) << std::endl;
    
      return 0;
    }