8

This is my very first question into this great knowledge exchange and I hope I find some help.

I try to implement a generic way to create PrintTo functions (later to be used in GoogleTest).

So the following code does just half of the job. It only prints the values of the defined struct Foo::Bar

#include <iostream>
#include <sstream>
#include <string>

#include <boost/fusion/container.hpp>
#include <boost/fusion/algorithm.hpp>
#include <boost/fusion/adapted/struct/define_assoc_struct.hpp>
#include <boost/fusion/include/define_assoc_struct.hpp>

namespace Foo
{
  namespace Keys
  {
    struct StringField;
    struct IntField;
  };
}

BOOST_FUSION_DEFINE_ASSOC_STRUCT(
  (Foo), Bar,
  (std::string, stringField, Foo::Keys::StringField)
  (int,         intField,    Foo::Keys::IntField))


struct fusion_printer_impl
{
  std::ostream& _os;

  fusion_printer_impl(std::ostream& os) 
    : _os(os) {}

  template <typename T>
  void operator() (T& v) const
  {
    _os << v << std::endl;
  }
};

void PrintTo(Foo::Bar const& v, std::ostream* os)
{
  boost::fusion::for_each(v, fusion_printer_impl(*os));
}

int main()
{
  Foo::Bar fb("Don't panic!", 42);
  std::ostringstream temp;

  PrintTo(fb, &temp);

  std::cout << temp.str() << std::endl;
}

So what I am looking for is a way to print automatically the Foo::Keys as well. I looked into the makro generated code of BOOST_FUSION_DEFINE_ASSOC_STRUCT and as far as I can see the Keys are available as static const char* boost::fusion::extension::struct_member_name::call().

I looked into the code of fusion::for_each and I so far I see only a way to 'replicate' the complete code so that fusion_printer_impl::operator() is been called with two parameters, Key and values. Before I go into that direction I would like to know if there is an easier ways to accomplish this.

I know that it is possible to define explicit a boost::fusion::map. Here one gets automatically access through the fusion::pair to Key type and value. But this is currently no option for me.

So any help here is welcome.

Felix Petriconi
  • 675
  • 5
  • 11

1 Answers1

3

Yours is a good question, hopefully somebody here will come to something cleaner than this:

...
struct fusion_printer_2
{
    typedef std::ostream* result_type;

    // Well, not really the intented use but...
    template<typename T>
    std::ostream* operator()(std::ostream const* out, const T& t) const
    {
        std::ostream* const_violated_out = const_cast<result_type>(out);
        (*const_violated_out) << 
            (std::string( typeid( typename boost::fusion::result_of::key_of<T>::type ).name() ) + ": " + boost::lexical_cast<std::string>(deref(t))) << std::endl;
        return const_violated_out;
    }
};

void PrintTo(Foo::Bar const& v, std::ostream* os)
{
  boost::fusion::iter_fold(v, os, fusion_printer_2());
}
...
dsign
  • 12,340
  • 6
  • 59
  • 82
  • 1
    Thanks for the answer. It helped me a lot further. By changing the result_type of the functor to std::string and streaming the result of boost::fusion::iter_fold directly to *os I can avoid the _ugly_ const_cast. – Felix Petriconi Apr 30 '13 at 16:28
  • Don't even mention it, leaving somebody there out to have his soul crushed by unfathomable abstractions and grim loads of compiler errors would be very inhuman.... – dsign Apr 30 '13 at 18:57
  • I know what you are talking about. At least VC10 and Clang have improved a lot. (I don't know about gcc). Having an error in usage of boost::spirit makes always fun :-( – Felix Petriconi Apr 30 '13 at 20:59