如何编写我自己的操纵器?
How to write my own manipulator?
假设我想为输入和输出编写自己的操纵器。
cin >> mymanip >> str;
或
cout << mymanip << str;
我希望 mymanip 做的是 切换大小写 我从输入中读取的字符并将结果分配给一个字符串。
所以,如果我输入 "QwErTy",我会在字符串中得到 "qWeRtY"。
这是一个非常基本的任务,只有一个功能,但我想了解更多关于操纵器的知识。
谁能给个线索?
谢谢。
操纵器所做的就是在 std::ios_base
基 class.
中设置相应的位
例如,std::setprecision()
操纵器只是 invokes std::ios_base::precision(),在被操纵的流上。
std::setprecision()
的实现几乎是可读的,在 gcc 的头文件中(很少见,对于 C++ 库模板实现):
inline _Setprecision setprecision(int __n)
{ return { __n }; }
std::setprecision()
returns 内部 std::_Precision
对象。然后,>>
(和 <<
运算符,这是相似的)运算符的简单模板重载,对于 std::_Precision
对象,处理魔术的其余部分:
template<typename _CharT, typename _Traits>
inline basic_istream<_CharT, _Traits>&
operator>>(basic_istream<_CharT, _Traits>& __is, _Setprecision __f)
{
__is.precision(__f._M_n);
return __is;
}
在您的情况下,std::ios_base
class 中没有任何位可以实现您想要的 input/output 转换。因此,操纵器本身在这里不起作用。
您要尝试做的事情需要一种完全不同、更复杂的方法:
std::[io]stream
的自定义子 class,它使用 std::streambuf
的自定义子class。
std::streambuf
subclass 从链式流读取或写入,如您所述转换输入或输出。
从自定义子程序读取或写入class最终从链式流读取或写入,相应地转换数据。
你不能那样做。您可以做的是将字符串作为参数的操纵器,即
std::cout << toggle(str);
std::cin >> toggle(str);
操纵器只是所谓的语法糖,即它可以比其他方式更方便地做事。例如,
std::cout << std::setw(5) << x <<;
会和
一样
std::cout.width(5);
std::cout << x;
但更方便,因为它允许与其他 <<
操作链接在一起。
现在,没有你想要的东西的格式化支持(交换大小写字符),因此也没有办法为此提供语法糖。
但是,如果操纵器可以将你的字符串作为参数,那么你当然可以实现你想要的,以操纵器实现的标准方式实现。例如,
struct toggle_output
{ std::string const&str; }
inline toggle_output toggle(std::string const&str)
{ return {str}; }
inline std::ostream& operator<<(std::ostream&out, toggle_output const&t)
{
for(auto c:t.str)
if (std::islower(c)) out<<std::toupper(c);
else if(std::isupper(c)) out<<std::tolower(c);
else out<<c;
return out;
}
struct toggle_input
{ std::string &str; }
inline toggle_input toggle(std::string&str)
{ return {str}; }
inline std::istream& operator>>(std::istream&in, toggle_input &t)
{
in >> t.str;
for(auto&c:t.str)
if (std::islower(c)) c=std::toupper(c);
else if(std::isupper(c)) c=std::tolower(c);
return in;
}
您可能还需要(以免混淆)
inline std::ostream& operator<<(std::ostream&out, toggle_input const&t)
{ return out<<toggle_output(t.str); }
正如其他答案所解释的那样,操纵器只是模仿现有的 std::ios_base
功能。
您的问题有一个简单的解决方案,但我不确定这是否可以称为操纵器:
struct toggle_in_helper
{
std::string & res;
};
toggle_in_helper toggle (std::string & res)
{
return {res};
}
std::istream & operator >> (std::istream & in, toggle_in_helper h)
{
in >> h.res;
for (auto & c : h.res)
// toggle the case of 'c'
;
return in;
}
也就是说,我们创建了一个具有重载 operator >>
的助手 class toggle_in_helper
来完成这项工作。
方法有点棘手 - 但可以做到,您可以为流添加自己的操纵器。
首先,你需要你的开关:
class toggle_t {};
constexpr toggle_t toggle;
接下来 - ostream
的版本(istream
的情况非常相似...):
将 toggle
放到 ostream
之后 - 你需要一些特殊的对象:
struct toggled_ostream
{
std::ostream& os;
};
inline toggled_ostream operator << (std::ostream& os, toggle_t)
{
return { os };
}
请注意,有人可能会将 toggle
放在错误的位置:cout << toggle << 123
- 因此它应该像普通流一样适用于所有其他类型:
template <typename T>
std::ostream& operator << (toggled_ostream tos, const T& v)
{
return tos.os << v;
}
所以 - 对于 char 类型(如 char
、const char*
、std::string
)编写你的切换重载。我给你 char
的版本 - 为 "longer" 类型编写版本应该不是问题:
std::ostream& operator << (toggled_ostream tos, char v)
{
char c = std::isupper(v) ? std::tolower(v)
: std::islower(v) ? std::toupper(v) : v;
return tos.os << c;
}
工作demo。
假设我想为输入和输出编写自己的操纵器。
cin >> mymanip >> str;
或
cout << mymanip << str;
我希望 mymanip 做的是 切换大小写 我从输入中读取的字符并将结果分配给一个字符串。
所以,如果我输入 "QwErTy",我会在字符串中得到 "qWeRtY"。
这是一个非常基本的任务,只有一个功能,但我想了解更多关于操纵器的知识。
谁能给个线索?
谢谢。
操纵器所做的就是在 std::ios_base
基 class.
例如,std::setprecision()
操纵器只是 invokes std::ios_base::precision(),在被操纵的流上。
std::setprecision()
的实现几乎是可读的,在 gcc 的头文件中(很少见,对于 C++ 库模板实现):
inline _Setprecision setprecision(int __n)
{ return { __n }; }
std::setprecision()
returns 内部 std::_Precision
对象。然后,>>
(和 <<
运算符,这是相似的)运算符的简单模板重载,对于 std::_Precision
对象,处理魔术的其余部分:
template<typename _CharT, typename _Traits>
inline basic_istream<_CharT, _Traits>&
operator>>(basic_istream<_CharT, _Traits>& __is, _Setprecision __f)
{
__is.precision(__f._M_n);
return __is;
}
在您的情况下,std::ios_base
class 中没有任何位可以实现您想要的 input/output 转换。因此,操纵器本身在这里不起作用。
您要尝试做的事情需要一种完全不同、更复杂的方法:
std::[io]stream
的自定义子 class,它使用std::streambuf
的自定义子class。std::streambuf
subclass 从链式流读取或写入,如您所述转换输入或输出。从自定义子程序读取或写入class最终从链式流读取或写入,相应地转换数据。
你不能那样做。您可以做的是将字符串作为参数的操纵器,即
std::cout << toggle(str);
std::cin >> toggle(str);
操纵器只是所谓的语法糖,即它可以比其他方式更方便地做事。例如,
std::cout << std::setw(5) << x <<;
会和
一样std::cout.width(5);
std::cout << x;
但更方便,因为它允许与其他 <<
操作链接在一起。
现在,没有你想要的东西的格式化支持(交换大小写字符),因此也没有办法为此提供语法糖。
但是,如果操纵器可以将你的字符串作为参数,那么你当然可以实现你想要的,以操纵器实现的标准方式实现。例如,
struct toggle_output
{ std::string const&str; }
inline toggle_output toggle(std::string const&str)
{ return {str}; }
inline std::ostream& operator<<(std::ostream&out, toggle_output const&t)
{
for(auto c:t.str)
if (std::islower(c)) out<<std::toupper(c);
else if(std::isupper(c)) out<<std::tolower(c);
else out<<c;
return out;
}
struct toggle_input
{ std::string &str; }
inline toggle_input toggle(std::string&str)
{ return {str}; }
inline std::istream& operator>>(std::istream&in, toggle_input &t)
{
in >> t.str;
for(auto&c:t.str)
if (std::islower(c)) c=std::toupper(c);
else if(std::isupper(c)) c=std::tolower(c);
return in;
}
您可能还需要(以免混淆)
inline std::ostream& operator<<(std::ostream&out, toggle_input const&t)
{ return out<<toggle_output(t.str); }
正如其他答案所解释的那样,操纵器只是模仿现有的 std::ios_base
功能。
您的问题有一个简单的解决方案,但我不确定这是否可以称为操纵器:
struct toggle_in_helper
{
std::string & res;
};
toggle_in_helper toggle (std::string & res)
{
return {res};
}
std::istream & operator >> (std::istream & in, toggle_in_helper h)
{
in >> h.res;
for (auto & c : h.res)
// toggle the case of 'c'
;
return in;
}
也就是说,我们创建了一个具有重载 operator >>
的助手 class toggle_in_helper
来完成这项工作。
方法有点棘手 - 但可以做到,您可以为流添加自己的操纵器。
首先,你需要你的开关:
class toggle_t {};
constexpr toggle_t toggle;
接下来 - ostream
的版本(istream
的情况非常相似...):
将 toggle
放到 ostream
之后 - 你需要一些特殊的对象:
struct toggled_ostream
{
std::ostream& os;
};
inline toggled_ostream operator << (std::ostream& os, toggle_t)
{
return { os };
}
请注意,有人可能会将 toggle
放在错误的位置:cout << toggle << 123
- 因此它应该像普通流一样适用于所有其他类型:
template <typename T>
std::ostream& operator << (toggled_ostream tos, const T& v)
{
return tos.os << v;
}
所以 - 对于 char 类型(如 char
、const char*
、std::string
)编写你的切换重载。我给你 char
的版本 - 为 "longer" 类型编写版本应该不是问题:
std::ostream& operator << (toggled_ostream tos, char v)
{
char c = std::isupper(v) ? std::tolower(v)
: std::islower(v) ? std::toupper(v) : v;
return tos.os << c;
}
工作demo。