2011-02-13 21 views
3

我很努力地找到如何使用boost::any创建一个打印函数,该函数可以首先使用模板打印任何类型。C++ boost :: any定义我自己的打印,

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

我需要先定义print()。 我希望有真正的operator <<任何,这个想法很简单:每个对象附加一个类 printer<T>与实例合适的T和更改此对象时any的值类型更改。 第一个技术问题是打印机对象依赖于T,而任何不是(也不应该是)类模板。

请问我真的需要一只手是为了今晚还是明天,我明天有一个期限,但我希望今晚能为此工作。

回答

3

有相当简单的方法来做到这一点,在 “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; 
    } 
}; 

然后使用any_out而不是boost::any

+0

谢谢帕维尔给我发送这个解决方案非常棒, –

+0

Karlsson先生(本书的作者)非常出色。我只记得他的书中有解决这个问题的方法。我很高兴我能帮上忙。 –

0

退房此线程Boost邮件列表: http://lists.boost.org/Archives/boost/2005/01/79232.php

它有一些想法,其中一些似乎有点OK其中一些并没有(对我来说)。总体而言,这似乎是一个难以完成的任务,因为(如该线程中提到的那样),某些类型永远不会流氓,但可以包含在boost::any对象中。

1

我不喜欢这样,我认为这是清洁,安全:

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; 
} 
} 

对于任何类型的你” d支持,只需确保已为其类型调用AnyWriter :: register(),并为其存在运算符< <。

例如:

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; 
} 

输出: 咕!

1

Pawel Zubrycki非常好的回答(提到BjörnKarlsson的书)。

但代码以下行有几个误区:

// ... 
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_); 

这里的帕维尔Zubrycki的答案的修正版本(部分作品...)

#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的模板实例。见下面

这使作为对以下recommened解决方案是:

#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; 
}