class 的自定义操纵器
Custom manipulator for class
我正在尝试编写带参数的流操纵器。
我有 class 和 3 int 的 CDate(年、月、日)。
所以我需要制作操纵器date_format(const char*)
。
例如:
CDate a(2006, 5, 15);
cout <<"DATE IS : " << date_format("%Y-hello-%d-world-%m-something-%d%d") << a;
输出将是:
DATE IS : 2006-hello-15-world-5-something-1515
我想我需要用到那个
ios_base & dummy_date_format_manipulator ( ios_base & x )
{
return x;
}
ios_base & ( * ( date_format ( const char * fmt ) ) )( ios_base & x )
{
return dummy_date_format_manipulator;
}
但我不知道怎么办。
带参数的操纵器与不带参数的操纵器的工作方式不同!只是 类 具有合适的输出运算符,它不是输出值而是操纵流的状态。要操纵流状态,您可能会设置一个合适的值,该值存储在与 dtream 关联的 iword()
或 pword()
中,并由输出运算符使用。
作为 chris suggested, I'd say that you should just use tm
而不是您的自定义日期 class:
tm a{0, 0, 0, 15, 5, 2006 - 1900};
cout << put_time(&a, "%Y-hello-%d-world-%m-something-%d%d");
如果您必须实现 get_time
and put_time
无法实现的自定义功能,那么您可能希望使用 tm
成员作为 class 的一部分,这样您就可以扩展已有的功能:
class CDate{
tm m_date;
public:
CDate(int year, int month, int day): m_date{0, 0, 0, day, month, year - 1900}{}
const tm& getDate() const{return m_date;}
};
ostream& operator<<(ostream& lhs, const CDate& rhs){
auto date = rhs.getDate();
return lhs << put_time(&a, "%Y-hello-%d-world-%m-something-%d%d");
}
然后您可以按如下方式使用 CDate
:
CDate a(2006, 5, 15);
cout << "DATE IS:" << a;
编辑:
再看你的问题,我认为你对插入运算符的工作原理有误解,不能同时传入对象和格式:https://msdn.microsoft.com/en-us/library/1z2f6c2k.aspx
如果您想指定格式但仍保留 CDate
class,我再次建议使用 put_time
:
cout << put_time(&a.getDate(), "%Y-hello-%d-world-%m-something-%d%d");
如果您再次坚持编写自己的格式接受函数,您将需要创建一个辅助程序 class,它可以内联构造并使用插入运算符支持它:
class put_CDate{
const CDate* m_pCDate;
const char* m_szFormat;
public:
put_CDate(const CDate* pCDate, const char* szFormat) : m_pCDate(pCDate), m_szFormat(szFormat) {}
const CDate* getPCDate() const { return m_pCDate; }
const char* getSZFormat() const { return m_szFormat; }
};
ostream& operator<<(ostream& lhs, const put_CDate& rhs){
return lhs << put_time(&rhs.getPCDate()->getDate(), rhs.getSZFormat());
}
您可以按如下方式使用它:
cout << put_CDate(&a, "%Y-hello-%d-world-%m-something-%d%d") << endl;
您可以为此使用 pword
数组。
C++ 中的每个 iostream 都有两个与之关联的数组。
ios_base::iword - array of ints
ios_base::pword - array of void* pointers
您可以在其中存储您自己的数据。要获得索引,它指的是所有 iword
和 pword
数组中的空元素,您应该使用函数 std::ios_base::xalloc()
。它 returns int 可以用作 *word
中的唯一索引。
您应该在启动时获取该索引一次,而不是将其用于 *word
.
的所有操作
然后编写您自己的 manip 将如下所示:
操纵器函数,接收对 ios_base
对象的引用和指向格式字符串的指针,只需将该指针存储在 pword
中
iosObject.pword(index_from_xalloc) = formatString
然后重载运算符<<
(>>
)以同样的方式从iostream对象中获取格式字符串。之后,您只需参考格式进行转换即可。
正如 Dietmar 所说,您可以将参数推入 iword(),但我发现这种解决方案既乏味又烦人。
我更喜欢将 lambda 函数安装为 iomanips 并使用它们直接调用各种 classes 方法或以其他方式就地构建自定义打印。为此,我为 mainpulators 创建了一个简单的 100 行模板 installer/helper class,它可以添加一个 lambda 函数作为任何 class..
的操纵器
因此,对于您的 CDate,您可以将 manip 定义为
std::ostream& dummy_date_format_manipulator (std::ostream& os)
{
CustomManip<CDate>::install(os,
[](std::ostream& oos, const CDate& a)
{
os << a.year()
<< "-hello-"
<< a.day()
<< "-world-"
<< a.month()
<< "-something-"
<< a.day() << a.day();
});
return os;
}
然后只需指示 << op 使用 manip 安装程序打印助手:
std::ostream& operator<<(std::ostream& os, const CDate& a)
{
CustomManip<CDate>::print(os, a);
return os;
}
你基本上完成了..
mainp 安装程序代码和一个完整的示例在我的博客 post 中:
http://code-slim-jim.blogspot.jp/2015/04/creating-iomanip-for-class-easy-way.html
但说句好话..这是你想要放在 .h 中的关键部分,而不是所有打印输出,以演示它是如何工作的:
//g++ -g --std=c++11 custom_class_manip.cpp
#include <iostream>
#include <ios>
#include <sstream>
#include <functional>
template <typename TYPE>
class CustomManip
{
private:
typedef std::function<void(std::ostream&, const TYPE&)> ManipFunc;
struct CustomManipHandle
{
ManipFunc func_;
};
static int handleIndex()
{
// the id for this Custommaniputors params
// in the os_base parameter maps
static int index = std::ios_base::xalloc();
return index;
}
public:
static void install(std::ostream& os, ManipFunc func)
{
CustomManipHandle* handle =
static_cast<CustomManipHandle*>(os.pword(handleIndex()));
// check if its installed on this ostream
if (handle == NULL)
{
// install it
handle = new CustomManipHandle();
os.pword(handleIndex()) = handle;
// install the callback so we can destroy it
os.register_callback (CustomManip<TYPE>::streamEvent,0);
}
handle->func_ = func;
}
static void uninstall(std::ios_base& os)
{
CustomManipHandle* handle =
static_cast<CustomManipHandle*>(os.pword(handleIndex()));
//delete the installed Custommanipulator handle
if (handle != NULL)
{
os.pword(handleIndex()) = NULL;
delete handle;
}
}
static void streamEvent (std::ios::event ev,
std::ios_base& os,
int id)
{
switch (ev)
{
case os.erase_event:
uninstall(os);
break;
case os.copyfmt_event:
case os.imbue_event:
break;
}
}
static void print(std::ostream& os, const TYPE& data)
{
CustomManipHandle* handle
= static_cast<CustomManipHandle*>(os.pword(handleIndex()));
if (handle != NULL)
{
handle->func_(os, data);
return;
}
data.printDefault(os);
}
};
当然,如果你确实需要这些参数,那么使用 CustomManip::make_installer(...) 函数来完成它,但是为此你必须访问博客..
我正在尝试编写带参数的流操纵器。
我有 class 和 3 int 的 CDate(年、月、日)。
所以我需要制作操纵器date_format(const char*)
。
例如:
CDate a(2006, 5, 15);
cout <<"DATE IS : " << date_format("%Y-hello-%d-world-%m-something-%d%d") << a;
输出将是:
DATE IS : 2006-hello-15-world-5-something-1515
我想我需要用到那个
ios_base & dummy_date_format_manipulator ( ios_base & x )
{
return x;
}
ios_base & ( * ( date_format ( const char * fmt ) ) )( ios_base & x )
{
return dummy_date_format_manipulator;
}
但我不知道怎么办。
带参数的操纵器与不带参数的操纵器的工作方式不同!只是 类 具有合适的输出运算符,它不是输出值而是操纵流的状态。要操纵流状态,您可能会设置一个合适的值,该值存储在与 dtream 关联的 iword()
或 pword()
中,并由输出运算符使用。
作为 chris suggested, I'd say that you should just use tm
而不是您的自定义日期 class:
tm a{0, 0, 0, 15, 5, 2006 - 1900};
cout << put_time(&a, "%Y-hello-%d-world-%m-something-%d%d");
如果您必须实现 get_time
and put_time
无法实现的自定义功能,那么您可能希望使用 tm
成员作为 class 的一部分,这样您就可以扩展已有的功能:
class CDate{
tm m_date;
public:
CDate(int year, int month, int day): m_date{0, 0, 0, day, month, year - 1900}{}
const tm& getDate() const{return m_date;}
};
ostream& operator<<(ostream& lhs, const CDate& rhs){
auto date = rhs.getDate();
return lhs << put_time(&a, "%Y-hello-%d-world-%m-something-%d%d");
}
然后您可以按如下方式使用 CDate
:
CDate a(2006, 5, 15);
cout << "DATE IS:" << a;
编辑:
再看你的问题,我认为你对插入运算符的工作原理有误解,不能同时传入对象和格式:https://msdn.microsoft.com/en-us/library/1z2f6c2k.aspx
如果您想指定格式但仍保留 CDate
class,我再次建议使用 put_time
:
cout << put_time(&a.getDate(), "%Y-hello-%d-world-%m-something-%d%d");
如果您再次坚持编写自己的格式接受函数,您将需要创建一个辅助程序 class,它可以内联构造并使用插入运算符支持它:
class put_CDate{
const CDate* m_pCDate;
const char* m_szFormat;
public:
put_CDate(const CDate* pCDate, const char* szFormat) : m_pCDate(pCDate), m_szFormat(szFormat) {}
const CDate* getPCDate() const { return m_pCDate; }
const char* getSZFormat() const { return m_szFormat; }
};
ostream& operator<<(ostream& lhs, const put_CDate& rhs){
return lhs << put_time(&rhs.getPCDate()->getDate(), rhs.getSZFormat());
}
您可以按如下方式使用它:
cout << put_CDate(&a, "%Y-hello-%d-world-%m-something-%d%d") << endl;
您可以为此使用 pword
数组。
C++ 中的每个 iostream 都有两个与之关联的数组。
ios_base::iword - array of ints
ios_base::pword - array of void* pointers
您可以在其中存储您自己的数据。要获得索引,它指的是所有 iword
和 pword
数组中的空元素,您应该使用函数 std::ios_base::xalloc()
。它 returns int 可以用作 *word
中的唯一索引。
您应该在启动时获取该索引一次,而不是将其用于 *word
.
然后编写您自己的 manip 将如下所示:
操纵器函数,接收对 ios_base
对象的引用和指向格式字符串的指针,只需将该指针存储在 pword
iosObject.pword(index_from_xalloc) = formatString
然后重载运算符<<
(>>
)以同样的方式从iostream对象中获取格式字符串。之后,您只需参考格式进行转换即可。
正如 Dietmar 所说,您可以将参数推入 iword(),但我发现这种解决方案既乏味又烦人。
我更喜欢将 lambda 函数安装为 iomanips 并使用它们直接调用各种 classes 方法或以其他方式就地构建自定义打印。为此,我为 mainpulators 创建了一个简单的 100 行模板 installer/helper class,它可以添加一个 lambda 函数作为任何 class..
的操纵器因此,对于您的 CDate,您可以将 manip 定义为
std::ostream& dummy_date_format_manipulator (std::ostream& os)
{
CustomManip<CDate>::install(os,
[](std::ostream& oos, const CDate& a)
{
os << a.year()
<< "-hello-"
<< a.day()
<< "-world-"
<< a.month()
<< "-something-"
<< a.day() << a.day();
});
return os;
}
然后只需指示 << op 使用 manip 安装程序打印助手:
std::ostream& operator<<(std::ostream& os, const CDate& a)
{
CustomManip<CDate>::print(os, a);
return os;
}
你基本上完成了..
mainp 安装程序代码和一个完整的示例在我的博客 post 中: http://code-slim-jim.blogspot.jp/2015/04/creating-iomanip-for-class-easy-way.html
但说句好话..这是你想要放在 .h 中的关键部分,而不是所有打印输出,以演示它是如何工作的:
//g++ -g --std=c++11 custom_class_manip.cpp
#include <iostream>
#include <ios>
#include <sstream>
#include <functional>
template <typename TYPE>
class CustomManip
{
private:
typedef std::function<void(std::ostream&, const TYPE&)> ManipFunc;
struct CustomManipHandle
{
ManipFunc func_;
};
static int handleIndex()
{
// the id for this Custommaniputors params
// in the os_base parameter maps
static int index = std::ios_base::xalloc();
return index;
}
public:
static void install(std::ostream& os, ManipFunc func)
{
CustomManipHandle* handle =
static_cast<CustomManipHandle*>(os.pword(handleIndex()));
// check if its installed on this ostream
if (handle == NULL)
{
// install it
handle = new CustomManipHandle();
os.pword(handleIndex()) = handle;
// install the callback so we can destroy it
os.register_callback (CustomManip<TYPE>::streamEvent,0);
}
handle->func_ = func;
}
static void uninstall(std::ios_base& os)
{
CustomManipHandle* handle =
static_cast<CustomManipHandle*>(os.pword(handleIndex()));
//delete the installed Custommanipulator handle
if (handle != NULL)
{
os.pword(handleIndex()) = NULL;
delete handle;
}
}
static void streamEvent (std::ios::event ev,
std::ios_base& os,
int id)
{
switch (ev)
{
case os.erase_event:
uninstall(os);
break;
case os.copyfmt_event:
case os.imbue_event:
break;
}
}
static void print(std::ostream& os, const TYPE& data)
{
CustomManipHandle* handle
= static_cast<CustomManipHandle*>(os.pword(handleIndex()));
if (handle != NULL)
{
handle->func_(os, data);
return;
}
data.printDefault(os);
}
};
当然,如果你确实需要这些参数,那么使用 CustomManip::make_installer(...) 函数来完成它,但是为此你必须访问博客..