C ++ boost :: any to define my own print,

I'm struggling to find how to do it so that I can use it boost::anyto create a print function that can print any type using a template in the first place.

template <typename T>
struct printer {
    void print(ostream& os, const boost::any& a);
}; 

I need to determine first print(). I want to have a real one operator <<for anyone. The idea is simple: we attach to each object an instance of a class printer<T>with a suitable T and change this object when the type of the value changes any. The first technical problem is that the printer object depends on T, while any is not (and should not) a class template.

Please, I really need a hand today or tomorrow. I have a deadline for tomorrow, but I want to work on it today.

+3
source share
4 answers

There is a fairly simple way to do this, described in " " Beyond the C ++ Standard Library: An Introduction to Boost ":

struct streamer {
  virtual void print(ostream &o, const boost::any &a) const =0;
  virtual streamer * clone() const = 0;
  virtual ~streamer() {}
};

template <class T>
struct streamer_impl: streamer{
  void print(ostream &o, const boost::any &a) const { o << *boost::any_cast<T>(a); }
  streamer *clone() const { return new streamer_impl<T>(); }
};

class any_out {
  streamer *streamer_;
  boost::any o_;
  void swap(any_out & r){
    std::swap(streamer_, r.streamer_);
    std::swap(o_, r.o_);
  }
public:
  any_out(): streamer_(0) {}
  template<class T> any_out(const T& value)
    : streamer_(new streamer_impl<T>()), o_(value) {}
  any_out(const any_out& a)
    : streamer_(a.streamer_ ? a.streamer_->clone() : 0), o_(a.o_) {}

  template <class T>
  any_out & operator=(const T& r) { 
    any_out(r).swap(*this);
    return *this;
  }
  ~any_out() { delete streamer_; }

  friend std::ostream &operator<<(std::ostream& o, const any_out & a) {
    if(a.streamer_)
      a.streamer_->print(o, a);
    return o;
  }
};

and then use any_outinstead boost::any.

+3
source

I do this in a way that I consider clean and safe:

any_extension.hpp:

namespace cpputil
{

struct AnyWriter
{
    /// Register a type with the AnyWriter.
    /// @pre T must have an ostream << operator available somewhere
    template<class T> static bool registerType()
    {
        return registeredTypes().emplace(std::type_index(typeid(T)),
                                         std::bind(&AnyWriter::write<T>,
                                                   std::placeholders::_1,
                                                   std::placeholders::_2)).second;
    }

    /// Write any registred object to a stream
    /// @pre Underlying type must have been registered with a call to AnyWriter::registerType<T>
    /// @param os is reference to a std::ostream
    /// @param anyObject is a reference to a boost::any
    static void writeAny(std::ostream& os, const boost::any& anyObject);
private:

    // A function object that converts an any to a type and streams it to an ostream
    using WriteFunction = std::function<void (std::ostream&, const boost::any&)>;

    // a map of typeinfo to WriteFunction
    using RegisteredTypes = std::unordered_map<std::type_index, WriteFunction >;

    // retrieve the WriteFunction map in a safe way
    static RegisteredTypes& registeredTypes();

    // Convert an any to a type, and write it to a stream
    template<class T> static void write(std::ostream& os, const boost::any& anyObject) {
        try {
            const T& typedObject = boost::any_cast<const T&>(anyObject);
            os << typedObject;
        }
        catch(boost::bad_any_cast& e) {
            os << "<exception in conversion: " << e.what() << ">";
        }
    }

};
}

namespace std {
    ostream& operator<<(ostream& os, const ::boost::any& anyObject);
}

any_extension.cpp:

#include "any_extension.h"
#include <string>

namespace cpputil {

namespace AnyWriterRegistration {
    const bool stringRegistered = AnyWriter::registerType<std::string>();
    const bool intRegistered = AnyWriter::registerType<int>();
    const bool doubleRegistered = AnyWriter::registerType<double>();
}



AnyWriter::RegisteredTypes& AnyWriter::registeredTypes()
{
    static RegisteredTypes _registrationMap;
    return _registrationMap;
}

void AnyWriter::writeAny(std::ostream &os, const boost::any &anyObject)
{
    auto registered = registeredTypes();
    auto iFind = registered.find(anyObject.type());
    if(iFind == registered.end()) {
        os << "<unregistered type: " << anyObject.type().name() << ">";
    }
    else {
        iFind->second(os, anyObject);
    }
}

}

namespace std {
ostream& operator<<(ostream& os, const ::boost::any& anyObject)
{
    if(anyObject.empty()) {
        os << "<empty>";
    }
    else {
        cpputil::AnyWriter::writeAny(os, anyObject);
    }
    return os;
}
}

For any type you want to support, just make sure AnyWriter :: register () has been called for its type, and the <operator exists for it.

For instance:

any_test.cpp:

struct chicken {};
std::operator<<(std::ostream& os, const chicken& aChicken) {
    os << "cluck!";
    return os;
}

namespace {
    const bool chickenRegistered = AnyWriter::register<Chicken>();
}

void chickenTest() {
    boost::any animal = chicken();
    std::cout << animal << std::endl;
}

Exit: cluck!

+1
source

( ).

:

// ...
o << *boost::any_cast<T>(a);    // should be:   o << *boost::any_cast<T>(&a);
// ...
a.streamer_->print(o, a);       // should be:   a.streamer_->print(o, a.o_);

​​ , (...)

#ifndef ANY_OUT_H
#define ANY_OUT_H

#include <iostream>
#include <boost/any.hpp>

struct streamer {
  virtual void print(std::ostream &o, const boost::any &a) const =0;
  virtual streamer * clone() const = 0;
  virtual ~streamer() {}
};

template <class T>
struct streamer_impl: streamer{
  void print(std::ostream &o, const boost::any &a) const { o << *boost::any_cast<T>(&a); }
  streamer *clone() const { return new streamer_impl<T>(); }
};

class any_out {
  streamer *streamer_;
  boost::any o_;
  void swap(any_out & r){
    std::swap(streamer_, r.streamer_);
    std::swap(o_, r.o_);
  }
public:
  any_out(): streamer_(0) {}
  template<class T> any_out(const T& value)
    : streamer_(new streamer_impl<T>()), o_(value) {}
  any_out(const any_out& a)
    : streamer_(a.streamer_ ? a.streamer_->clone() : 0), o_(a.o_) {}

  template <class T>
  any_out & operator=(const T& r) {
    any_out(r).swap(*this);
    return *this;
  }
  ~any_out() { delete streamer_; }

  friend std::ostream &operator<<(std::ostream& o, const any_out & a);
};

std::ostream &operator<<(std::ostream& o, const any_out & a) {
  if(a.streamer_)
    a.streamer_->print(o, a.o_);
  return o;
}

#endif

:

{
   any_out a = 5;
   std::cout << a << std::endl;
}

!!!!

:

int main()
{
  char str[] = "mystring";
  any_out a = str;
  std::cout << a << std::endl;

  a = "myconststring";
  std::cout << a << std::endl;
}

.

??

, ,

any_out(const T& value)

new streamer_impl<T>()

, ..

  any_out(const T value)

... .

- streamer_impl. .

:

#ifndef ANY_OUT_H
#define ANY_OUT_H

#include <iostream>
#include <boost/any.hpp>

struct streamer {
  virtual void print(std::ostream &o, const boost::any &a) const =0;
  virtual streamer * clone() const = 0;
  virtual ~streamer() {}
};

template <class T>
struct streamer_impl: streamer{
  void print(std::ostream &o, const boost::any &a) const { o << boost::any_cast<T>(a); }
  streamer *clone() const { return new streamer_impl<T>(); }
};

class any_out {
  boost::any o_;
  streamer *streamer_;
  void swap(any_out & r){
    std::swap(streamer_, r.streamer_);
    std::swap(o_, r.o_);
  }
public:
  any_out(): streamer_(0) {}

  template<class T> any_out(const T& value)
    : o_(value),
#if 1
      streamer_(new streamer_impl<typename std::decay<decltype(value)>::type>)
#else
      streamer_((o_.type() == typeid(const char *))
                ? static_cast<streamer *>(new streamer_impl<const char *>)
                : static_cast<streamer *>(new streamer_impl<T>))
#endif
  {
  }

  // template<class T> any_out(const T value)
  //   : o_(value),
  //     streamer_(new streamer_impl<T>)
  // {
  // }

  any_out(const any_out& a)
    : o_(a.o_), streamer_(a.streamer_ ? a.streamer_->clone() : 0) {}

  template <class T>
  any_out & operator=(const T& r) {
    any_out(r).swap(*this);
    return *this;
  }
  ~any_out() { delete streamer_; }

  friend std::ostream &operator<<(std::ostream& o, const any_out & a);
};

std::ostream &operator<<(std::ostream& o, const any_out & a) {
  if(a.streamer_)
    a.streamer_->print(o, a.o_);
  return o;
}

#endif

-, , ( " " ):

int main()
{
  char str[] = "mystring";
  any_out a = str;
  std::cout << a << std::endl;

  a = "myconststring";
  std::cout << a << std::endl;
}
+1

Boost: http://lists.boost.org/Archives/boost/2005/01/79232.php

It has several ideas, some of which seem like OK, and some of them are not (for me). In general, however, this seems like a daunting task to achieve a general approach, because (as mentioned in this thread) some types will never be available, but may be contained in the object boost::any.

0
source

Source: https://habr.com/ru/post/1791847/


All Articles