如何打印未知类型的对象 我有C++中的一个模板化容器类,它类似于一个STD::map(它基本上是一个围绕STD::MAP的线程安全包装)。我想写一个成员函数,它转储关于映射中条目的信息。然而,很明显,我不知道地图中对象的类型或它们的键。目标是能够处理基本类型(整数、字符串)以及我特别感兴趣的一些特定类类型。对于任何其他类,我希望至少编译,最好做一些智能化的事情,比如打印对象的地址。到目前为止,我的方法与以下类似(请注意,我实际上没有编译这个或任何东西……): 模板 类线程安全映射 { 地图存储地图; ... dumpkey() { for(std::map::iterator it=storageMap.begin(); it!=storageMap.end(); ++(it) { std::cout first
您可以提供一个模板化的如何打印未知类型的对象 我有C++中的一个模板化容器类,它类似于一个STD::map(它基本上是一个围绕STD::MAP的线程安全包装)。我想写一个成员函数,它转储关于映射中条目的信息。然而,很明显,我不知道地图中对象的类型或它们的键。目标是能够处理基本类型(整数、字符串)以及我特别感兴趣的一些特定类类型。对于任何其他类,我希望至少编译,最好做一些智能化的事情,比如打印对象的地址。到目前为止,我的方法与以下类似(请注意,我实际上没有编译这个或任何东西……): 模板 类线程安全映射 { 地图存储地图; ... dumpkey() { for(std::map::iterator it=storageMap.begin(); it!=storageMap.end(); ++(it) { std::cout first,c++,stream,C++,Stream,您可以提供一个模板化的,我最初只有一种更规范的使用方法 目前,如果未找到显式插入运算符,则模板化插入运算符将生效并定义完美匹配;这将破坏现有隐式转换的任何可能性 必须做的是使模板插入运算符成为转换(同时保持其通用性),以便可以考虑其他转换。一旦找不到其他转换,则将其转换为通用插入运算符 实用程序代码如下所示: #include <iosfwd> #include <memory> namespace outputter_any_detail { // your
,我最初只有一种更规范的使用方法
目前,如果未找到显式插入运算符,则模板化插入运算符将生效并定义完美匹配;这将破坏现有隐式转换的任何可能性
必须做的是使模板插入运算符成为转换(同时保持其通用性),以便可以考虑其他转换。一旦找不到其他转换,则将其转换为通用插入运算符
实用程序代码如下所示:
#include <iosfwd>
#include <memory>
namespace outputter_any_detail
{
// your generic output function
template <typename T>
std::ostream& output_generic(std::ostream& pStream, const T& pX)
{
// note: safe from recursion. if you accidentally try
// to output pX again, you'll get a compile error
return pStream << "unknown type at address: " << &pX;
}
// any type can be converted to this type,
// but all other conversions will be
// preferred before this one
class any
{
public:
// stores a type for later output
template <typename T>
any(const T& pX) :
mPtr(new any_holder<T>(pX))
{}
// output the stored type generically
std::ostream& output(std::ostream& pStream) const
{
return mPtr->output(pStream);
}
private:
// hold any type
class any_holder_base
{
public:
virtual std::ostream& output(std::ostream& pStream) const = 0;
virtual ~any_holder_base(void) {}
};
template <typename T>
class any_holder : public any_holder_base
{
public:
any_holder(const T& pX) :
mX(pX)
{}
std::ostream& output(std::ostream& pStream) const
{
return output_generic(pStream, mX);
}
private:
const T& mX;
any_holder& operator=(const any_holder&);
};
std::auto_ptr<any_holder_base> mPtr;
any& operator=(const any&);
};
// hidden so the generic output function
// cannot accidentally call this fall-back
// function (leading to infinite recursion)
namespace detail
{
// output a type converted to any. this being a conversion allows
// other conversions to partake in overload resolution
std::ostream& operator<<(std::ostream& pStream, const any& pAny)
{
return pAny.output(pStream);
}
}
// a transfer class, to allow
// a unique insertion operator
template <typename T>
class outputter_any
{
public:
outputter_any(const T& pX) :
mX(pX)
{}
const T& get(void) const
{
return mX;
}
private:
const T& mX;
outputter_any& operator=(const outputter_any&);
};
// this is how outputter_any's get outputted,
// found outside the detail namespace by ADL
template <typename T>
std::ostream& operator<<(std::ostream& pStream, const outputter_any<T>& pX)
{
// bring in the fall-back insertion operator
using namespace detail;
// either a specifically defined operator,
// or the generic one via a conversion to any
return pStream << pX.get();
}
}
// construct an outputter_any
template <typename T>
outputter_any_detail::outputter_any<T> output_any(const T& pX)
{
return outputter_any_detail::outputter_any<T>(pX);
}
如果某个类不支持打印,请告诉我。什么都不是选项吗?如果某个类不允许输出,为什么要尝试输出它?对不支持打印的类不执行任何操作都是可以接受的。当前,我收到一个模板实例化的编译错误,其中一个模板参数不支持打印。我希望至少,在类不支持打印的情况下,编译不会崩溃。我喜欢你的答案。我接受了Staffan的答案,因为对于我的应用程序来说,更简单的实现似乎已经足够了。不幸的是,我还没有投票,否则你肯定会得到一个。谢谢你的时间!我偶然发现了你的解决方案,我发现它不起作用很好。看,它不能正确地处理int*
和B
@jpalecek:虽然我认为你提出了一个重要的观点,但我想纠正你的术语:你说“这个打印”未知类型“即使是已知的类型”但是无论是B
还是int*
都没有插入操作;也就是说,它们都不是“已知的”。您的意思是“解决方案,因为它使用模板,比任何转换更喜欢通用解决方案”,这解释得更好。B
可以转换为A
,但模板插入操作更匹配,int*
可以转换为void*
,但模板插入操作也更匹配。我会修复它。@jpalecek:应该会修复它。将模板函数制作成模板类+转换使代码有点冗长,但这并不太难,也不需要经常查看。:)哦,是的,我本可以意识到。我试图将其转换为可用于SFINAE的内容,并敦促不要使用额外的名称空间-这是一个坏主意。有了额外的名称空间,很容易:好的,这很容易“未知类型”,即使是已知的类型。参见我对GMan解决方案的评论。
#include <iostream>
namespace detail
{
template<typename T, typename CharT, typename Traits>
std::basic_ostream<CharT, Traits> &
operator<<(std::basic_ostream<CharT, Traits> &os, const T &)
{
const char s[] = "<unknown-type>";
os.write(s, sizeof(s));
return os;
}
}
struct Foo {};
int main()
{
using namespace detail;
std::cout << 2 << "\n" << Foo() << std::endl;
return 0;
}
2
<unknown-type>
#include <iosfwd>
#include <memory>
namespace outputter_any_detail
{
// your generic output function
template <typename T>
std::ostream& output_generic(std::ostream& pStream, const T& pX)
{
// note: safe from recursion. if you accidentally try
// to output pX again, you'll get a compile error
return pStream << "unknown type at address: " << &pX;
}
// any type can be converted to this type,
// but all other conversions will be
// preferred before this one
class any
{
public:
// stores a type for later output
template <typename T>
any(const T& pX) :
mPtr(new any_holder<T>(pX))
{}
// output the stored type generically
std::ostream& output(std::ostream& pStream) const
{
return mPtr->output(pStream);
}
private:
// hold any type
class any_holder_base
{
public:
virtual std::ostream& output(std::ostream& pStream) const = 0;
virtual ~any_holder_base(void) {}
};
template <typename T>
class any_holder : public any_holder_base
{
public:
any_holder(const T& pX) :
mX(pX)
{}
std::ostream& output(std::ostream& pStream) const
{
return output_generic(pStream, mX);
}
private:
const T& mX;
any_holder& operator=(const any_holder&);
};
std::auto_ptr<any_holder_base> mPtr;
any& operator=(const any&);
};
// hidden so the generic output function
// cannot accidentally call this fall-back
// function (leading to infinite recursion)
namespace detail
{
// output a type converted to any. this being a conversion allows
// other conversions to partake in overload resolution
std::ostream& operator<<(std::ostream& pStream, const any& pAny)
{
return pAny.output(pStream);
}
}
// a transfer class, to allow
// a unique insertion operator
template <typename T>
class outputter_any
{
public:
outputter_any(const T& pX) :
mX(pX)
{}
const T& get(void) const
{
return mX;
}
private:
const T& mX;
outputter_any& operator=(const outputter_any&);
};
// this is how outputter_any's get outputted,
// found outside the detail namespace by ADL
template <typename T>
std::ostream& operator<<(std::ostream& pStream, const outputter_any<T>& pX)
{
// bring in the fall-back insertion operator
using namespace detail;
// either a specifically defined operator,
// or the generic one via a conversion to any
return pStream << pX.get();
}
}
// construct an outputter_any
template <typename T>
outputter_any_detail::outputter_any<T> output_any(const T& pX)
{
return outputter_any_detail::outputter_any<T>(pX);
}
#include <iostream>
#include "output_any.hpp"
struct foo {};
struct A {};
struct B : A {};
std::ostream& operator<<(std::ostream& o, const A&)
{
return o << "A";
}
int main(void)
{
foo f;
int i = 5;
B b;
/*
Expected output:
unknown type at address: [address]
5
[address]
A
*/ // output via...
std::cout << output_any(f) << std::endl; // generic
std::cout << output_any(i) << std::endl; // int
std::cout << output_any(&i) << std::endl;// void*
std::cout << output_any(b) << std::endl; // const A&
}