因此,感谢C ++ 11,现在可以将宏,用户定义的文字,lambdas等组合在一起,以创建最接近“语法糖”的语法。一个例子是
if (A contains B)
当然这很容易。
cout <<("hello"_s contains "ello"_s)<<endl;
表达式转换为bool,其中contains是一个自定义结构,它将左侧和右侧作为参数。当然结构重载operator +来先获取自定义字符串文字,然后返回自身,然后是结构本身的operator +。
struct contains_struct {
string lhs;
string rhs;
void set_lhs(string lhs) { this->lhs = lhs; }
void set_rhs(string rhs) { this->rhs = rhs; }
operator bool() const {
return string::npos != lhs.find(rhs);
}
} contains_obj;
contains_struct& operator+(const string& lhs, const contains_struct& rhs) {
contains_obj.set_lhs(lhs);
return contains_obj;
}
contains_struct& operator+(const contains_struct& lhs, const string& rhs) {
contains_obj.set_rhs(rhs);
return contains_obj;
}
#define contains +contains_obj+
现在我觉得我想要更进一步。
怎么样?(x in a) perform cube
这不是列表理解,但这是一个很好的例子吗?起初我说,好吧,我必须去stackoverflow询问自定义运算符优先级,但它直接将它放在括号中,因为没有一个心智正常的人会使用我的代码。相反,我扩展了我的另一个例子并将'in'和'perform'作为自定义结构,就像'contains'一样。
你可以进一步模拟它,使x可以是任何数字索引,和任何容器一样,但为了简单起见,我将x作为整数和一个int的向量。到目前为止,它实际上并没有将局部变量x作为参数,而是在运算符string()函数中本地使用它。
为简化起见,我将表达式的结果存储在字符串中,如此
operator string() const {
string s = "";
for (int x : lhs.rhs)
s += to_string(rhs(x)) + string("\n");
return s;
}
感谢另一个问题:Overloading assignment operator for type deduction
我意识到返回它的一个实际用途如下:
struct result_struct {
vector<int> results;
result_struct(vector<int> results) { this->results = results; }
};
...
operator result_struct() const {
vector<int> tmp;
for (int x : lhs.rhs)
tmp.push_back(rhs(x));
return result_struct(tmp);
}
...
result_struct result_2 = (x in a) perform cube;
for (int x : result_2.results)
cout <<x<<endl;
感谢milleniumbug's answer,我可以这样做:
struct for_obj
{
int _lhs;
std::vector<int> _rhs;
for_obj(int lhs, std::vector<int> rhs)
: _lhs(lhs), _rhs(rhs) { }
};
INFIX_OPERATOR(for_obj, in_op, int, std::vector<int>)
{
return for_obj(lhs(), rhs());
}
#define in + in_op() +
INFIX_OPERATOR(int, perform_op, for_obj, std::function<int(int)>)
{
for (int i = 0; i < lhs()._rhs.size(); i++)
rhs()(lhs()._rhs[i]);
return 0;
}
#define perform + perform_op() +
有两点需要注意。首先,我返回一个int,以便我可以将它分配给一个虚拟变量来让它执行。我总是可以做我之前做过的result_struct事情,或者返回一个std :: function对象来自己调用它,但我会重复自己。另一个警告是,因为宏中有如此多的consts,你不能修改lhs(它不允许你指定迭代器)。
考虑到所有因素,以下工作符合预期。
int x = 0;
std::vector<int> nums = { 1, 2, 3 };
auto cube = [] (int x)
{
std::cout << x * x * x << std::endl;
return x * x * x;
};
int i = (x in nums) perform cube;
新版本
class PerformObj {
int counter;
public:
PerformObj() : counter(0) { }
~PerformObj() { }
InObj lhs;
std::function<int(int)> rhs;
operator int() const {
return rhs(lhs.rhs[counter]);
}
} performobj;
#define perform + performobj +
PerformObj& operator+(const InObj& lhs, PerformObj& rhs) {
rhs.lhs = lhs;
return rhs;
}
PerformObj& operator+(PerformObj& lhs, const std::function<int(int)>& rhs) {
lhs.rhs = rhs;
return lhs;
}
int main()
{
std::vector<int> nums = {1,2,3};
int x = 0;
auto cube = [] (int n) {
return n * n * n;
};
std::cout << x in nums perform cube << std::endl;
}
explicit operator std::vector<int>() const {
std::vector<int> temp;
for (int i = 0; i < lhs.rhs.size(); i++) {
temp.push_back(rhs(lhs.rhs[i]));
}
return temp;
}
int y = 0;
std::cout << y in static_cast<std::vector<int>>(x in nums perform cube) perform std::function<int(int)>([] (int i) -> int {
return i;
}) << std::endl;
我是否应该这样做而不是中缀运算符,有"String literal"s.contains "Other string literal"s
之类的后缀运算符,或者是函数样式,"String literal"s.contains("Other string literal"s)
?
如何改进我的代码以使其更具可扩展性?就像现在一样,它受到严重污染。有没有更好/更普遍/更少笨重的方式来做到这一点?例如,要概括表达式,以便我不需要定义语句或重用代码。
答案 0 :(得分:12)
很难看出这里提出的问题是什么,假设最新的编辑有所有问题。
我应该这样做,而不是中缀运算符,有postfix 运算符,如“String literal”s.contains“Other string literal”,或 做它的功能样式,“字符串文字”s.contains(“其他字符串 文字“S)?
是。 "String literal"s.contains("Other string literal"s)
是最好的方法 - 对于C ++程序员来说简洁明了,对其他语言的程序员来说很清楚(Java和Python字符串都有方法),并且没有使用模板魔法和宏魔法。
如何改进我的代码以使其更具可扩展性?因为它是对的 现在,它已经被污染了。是否更好/更普遍/更少 笨重的方式来做到这一点?例如,为了概括表达式 我不需要定义语句或重用代码。
是的!但只是在某种程度上(在那里和这里删除了不必要的争议):
#define INFIX_OPERATOR(rettype, name, LT, RT) \
struct name\
{\
private:\
LT* left;\
RT* right;\
\
protected:\
LT& lhs() const { return *left; }\
RT& rhs() const { return *right; }\
\
public: \
friend name operator+(LT& lhs, name && op)\
{\
op.left = &lhs;\
return op;\
}\
\
friend name operator+(name && op, RT& rhs)\
{\
op.right = &rhs;\
return op;\
}\
\
name () : left(nullptr), right(nullptr) {}\
\
operator rettype() const;\
};\
\
inline name :: operator rettype() const
然后你可以像这样创建你的中缀运算符:
#include <iostream>
#include <string>
INFIX_OPERATOR(bool, contains_op, const std::string, const std::string)
{
return std::string::npos != lhs().find(rhs());
}
#define contains + contains_op() +
int main()
{
std::string a = "hello";
std::string b = "hell";
if(a contains b)
std::cout << "YES";
}
请注意,没有办法避免#define contains指令,因为没有办法用另一个宏指令创建宏指令。
如果有的话,有什么实际的好处(忽略全部 使用它作为真实世界代码的合理性。我的意思是你能得到什么 除了它用于我正在使用它,禁止娱乐目的?) 说我的朋友,而不是学习C ++,想要一个简单的抽象 他的Bash或Perl经验的界面,但我想 协作而不需要在gcc之外编译/链接。那 他可以编写C ++的“脚本”或“代码”,并编译和编译 将它与我的程序/库/接口链接,无论如何。
您似乎正在尝试在另一种语言之上创建一种语言。准备
如果你的朋友想要用Perl编程,那就让他做吧。这些语言很容易与C语言接口。
如果你正在尝试创建一种语言,因为其他语言无法干净地表达你想要做的事情,解析器生成器(Flex / Bison,ANTLR)和LLVM使它变得容易。
如果创建解析器过度,请查看D语言混合。它们接受在编译时创建的字符串,然后将其编译为就像直接插入一样。
下面...
std::vector<void> myarr;
相当于:
import std.stdio;
int main()
{
mixin(`write("Hello world");`); //`contents` is a raw string literal
return 0; //so is r"contents"
}
这只是一个简单的例子。你可以拥有解析字符串的函数:
import std.stdio;
int main()
{
write("Hello world");
return 0;
}
1 - 这是它的外观(gcc 4.7.2):
mixin(user1508519s_language(r"(x in a) perform cube"));