定义新的中缀操作符
Defining new infix operators
所以多亏了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可以是任何数值索引,a可以是任何容器,但为了简单起见,我将x保留为整数,而将a保留为整型向量。到目前为止,它实际上并没有将局部变量x作为参数,而是在操作符string()函数中局部使用它。
为了简化,我将表达式的结果存储在字符串中,如下所示
operator string() const {
string s = "";
for (int x : lhs.rhs)
s += to_string(rhs(x)) + string("n");
return s;
}
由于另一个问题:类型演绎的重载赋值操作符
我意识到将它作为赋值返回的一个实际用途如下:
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的回答,我可以做到:
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对象来调用它自己,但我将重复自己。另一个注意事项是,因为宏中有很多const,所以不能修改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)
?
我该如何改进我的代码使其更具可扩展性?就像现在一样,污染很严重。有没有更好的/更普遍的/不那么笨拙的方法来做到这一点?例如,泛化表达式,这样我就不需要定义语句或重用代码。
假设最新的编辑包含了所有的问题,很难看出这里提出的问题是什么。
我是否应该让后缀操作符代替中缀操作符操作符,比如"字符串字面值"。包含"其他字符串字面值",或do it function style, "String literal"。包含("其他字符串文字的)?
是的。"String literal"s.contains("Other string literal"s)
是最好的方法-简洁,对c++程序员清楚,对其他语言的程序员清楚(Java和Python字符串有方法),没有模板魔术或宏魔术使用。
我该如何改进我的代码使其更具可扩展性?因为它是正确的现在,污染很严重。有没有更好的/更概括的/更少的笨拙的做法?例如,要概括表达式so我不需要定义语句或重用代码。
是的!但只是在一定程度上(删除了那里和这里不必要的const):
#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++编写"脚本"或"代码",然后进行编译链接到我的程序/库/接口,等等。
你似乎在尝试在另一种语言之上创建一种语言。准备
- 长时间测试你的语言。
- 令人尴尬的坏诊断信息。试着编译这个:
std::vector<void> myarr;
1然后用宏包装它。然后将其封装在另一个模板中。然后在另一个宏中…你懂的。 - 显示处理代码的调试工具。
- 即使你的语言与自己完美地集成在一起,你仍然有c++要照顾,它有大量的规则和复杂的类型系统。毕竟,所有抽象都有漏洞。
如果你的朋友想用Perl编程,就让他做吧。这些语言很容易与c语言交互。
如果你正在尝试创建一种语言,因为其他语言不能清晰地表达你想要做的事情,解析器生成器(Flex/Bison, ANTLR)和LLVM使它变得容易。
如果创建解析器是多余的,请查看D语言混合。它们接受在编译时创建的字符串,然后将其编译为直接插入的字符串。
这里……
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;
}
这只是一个简单的例子。您可以使用解析字符串的函数:
mixin(user1508519s_language(r"(x in a) perform cube"));
1 -这是它的样子(gcc 4.7.2):
In file included from c:__mojeprogmingwbin../lib/gcc/mingw32/4.7.2/include/
c++/bits/stl_construct.h:63:0,
from c:__mojeprogmingwbin../lib/gcc/mingw32/4.7.2/include/
c++/vector:63,
from #templateerrors2.cpp:1:
c:__mojeprogmingwbin../lib/gcc/mingw32/4.7.2/include/c++/ext/alloc_traits.h
: In instantiation of 'struct __gnu_cxx::__alloc_traits<std::allocator<void> >':
c:__mojeprogmingwbin../lib/gcc/mingw32/4.7.2/include/c++/bits/stl_vector.h:
76:28: required from 'struct std::_Vector_base<void, std::allocator<void> >'
c:__mojeprogmingwbin../lib/gcc/mingw32/4.7.2/include/c++/bits/stl_vector.h:
208:11: required from 'class std::vector<void>'
#templateerrors2.cpp:5:19: required from here
c:__mojeprogmingwbin../lib/gcc/mingw32/4.7.2/include/c++/ext/alloc_traits.h
:189:53: error: no type named 'reference' in 'class std::allocator<void>'
c:__mojeprogmingwbin../lib/gcc/mingw32/4.7.2/include/c++/ext/alloc_traits.h
:190:53: error: no type named 'const_reference' in 'class std::allocator<void>'
In file included from c:__mojeprogmingwbin../lib/gcc/mingw32/4.7.2/include/
c++/vector:65:0,
from #templateerrors2.cpp:1:
c:__mojeprogmingwbin../lib/gcc/mingw32/4.7.2/include/c++/bits/stl_vector.h:
In instantiation of 'class std::vector<void>':
#templateerrors2.cpp:5:19: required from here
c:__mojeprogmingwbin../lib/gcc/mingw32/4.7.2/include/c++/bits/stl_vector.h:
292:7: error: forming reference to void
c:__mojeprogmingwbin../lib/gcc/mingw32/4.7.2/include/c++/bits/stl_vector.h:
467:7: error: forming reference to void
c:__mojeprogmingwbin../lib/gcc/mingw32/4.7.2/include/c++/bits/stl_vector.h:
684:7: error: invalid parameter type 'std::vector<void>::value_type {aka void}'
c:__mojeprogmingwbin../lib/gcc/mingw32/4.7.2/include/c++/bits/stl_vector.h:
684:7: error: in declaration 'void std::vector<_Tp, _Alloc>::resize(std::vector<
_Tp, _Alloc>::size_type, std::vector<_Tp, _Alloc>::value_type)'
c:__mojeprogmingwbin../lib/gcc/mingw32/4.7.2/include/c++/bits/stl_vector.h:
881:7: error: forming reference to void
In file included from c:__mojeprogmingwbin../lib/gcc/mingw32/4.7.2/include/
c++/vector:70:0,
from #templateerrors2.cpp:1:
c:__mojeprogmingwbin../lib/gcc/mingw32/4.7.2/include/c++/bits/vector.tcc:10
8:5: error: forming reference to void
In file included from c:__mojeprogmingwbin../lib/gcc/mingw32/4.7.2/include/
c++/vector:65:0,
from #templateerrors2.cpp:1:
c:__mojeprogmingwbin../lib/gcc/mingw32/4.7.2/include/c++/bits/stl_vector.h:
1003:7: error: forming reference to void
c:__mojeprogmingwbin../lib/gcc/mingw32/4.7.2/include/c++/bits/stl_vector.h:
1179:7: error: forming reference to void
In file included from c:__mojeprogmingwbin../lib/gcc/mingw32/4.7.2/include/
c++/vector:70:0,
from #templateerrors2.cpp:1:
c:__mojeprogmingwbin../lib/gcc/mingw32/4.7.2/include/c++/bits/vector.tcc:21
6:5: error: forming reference to void
c:__mojeprogmingwbin../lib/gcc/mingw32/4.7.2/include/c++/bits/vector.tcc:43
9:5: error: forming reference to void
c:__mojeprogmingwbin../lib/gcc/mingw32/4.7.2/include/c++/bits/vector.tcc:31
6:5: error: forming reference to void
In file included from c:__mojeprogmingwbin../lib/gcc/mingw32/4.7.2/include/
c++/vector:65:0,
from #templateerrors2.cpp:1:
c:__mojeprogmingwbin../lib/gcc/mingw32/4.7.2/include/c++/bits/stl_vector.h:
In instantiation of 'std::_Vector_base<_Tp, _Alloc>::~_Vector_base() [with _Tp
= void; _Alloc = std::allocator<void>]':
c:__mojeprogmingwbin../lib/gcc/mingw32/4.7.2/include/c++/bits/stl_vector.h:
247:15: required from 'std::vector<_Tp, _Alloc>::vector() [with _Tp = void; _A
lloc = std::allocator<void>]'
#templateerrors2.cpp:5:19: required from here
c:__mojeprogmingwbin../lib/gcc/mingw32/4.7.2/include/c++/bits/stl_vector.h:
161:9: error: invalid use of 'void'
c:__mojeprogmingwbin../lib/gcc/mingw32/4.7.2/include/c++/bits/stl_vector.h:
In instantiation of 'void std::_Vector_base<_Tp, _Alloc>::_M_deallocate(std::_V
ector_base<_Tp, _Alloc>::pointer, std::size_t) [with _Tp = void; _Alloc = std::a
llocator<void>; std::_Vector_base<_Tp, _Alloc>::pointer = void*; std::size_t = u
nsigned int]':
c:__mojeprogmingwbin../lib/gcc/mingw32/4.7.2/include/c++/bits/stl_vector.h:
161:9: required from 'std::_Vector_base<_Tp, _Alloc>::~_Vector_base() [with _T
p = void; _Alloc = std::allocator<void>]'
c:__mojeprogmingwbin../lib/gcc/mingw32/4.7.2/include/c++/bits/stl_vector.h:
247:15: required from 'std::vector<_Tp, _Alloc>::vector() [with _Tp = void; _A
lloc = std::allocator<void>]'
#templateerrors2.cpp:5:19: required from here
c:__mojeprogmingwbin../lib/gcc/mingw32/4.7.2/include/c++/bits/stl_vector.h:
175:4: error: 'struct std::_Vector_base<void, std::allocator<void> >::_Vector_im
pl' has no member named 'deallocate'
In file included from c:__mojeprogmingwbin../lib/gcc/mingw32/4.7.2/include/
c++/bits/stl_algobase.h:66:0,
from c:__mojeprogmingwbin../lib/gcc/mingw32/4.7.2/include/
c++/vector:61,
from #templateerrors2.cpp:1:
c:__mojeprogmingwbin../lib/gcc/mingw32/4.7.2/include/c++/bits/stl_iterator_
base_types.h: In instantiation of 'struct std::iterator_traits<void*>':
c:__mojeprogmingwbin../lib/gcc/mingw32/4.7.2/include/c++/bits/stl_construct
.h:127:24: required from 'void std::_Destroy(_ForwardIterator, _ForwardIterato
r) [with _ForwardIterator = void*]'
c:__mojeprogmingwbin../lib/gcc/mingw32/4.7.2/include/c++/bits/stl_construct
.h:155:7: required from 'void std::_Destroy(_ForwardIterator, _ForwardIterator
, std::allocator<_T2>&) [with _ForwardIterator = void*; _Tp = void]'
c:__mojeprogmingwbin../lib/gcc/mingw32/4.7.2/include/c++/bits/stl_vector.h:
403:9: required from 'std::vector<_Tp, _Alloc>::~vector() [with _Tp = void; _A
lloc = std::allocator<void>]'
#templateerrors2.cpp:5:19: required from here
c:__mojeprogmingwbin../lib/gcc/mingw32/4.7.2/include/c++/bits/stl_iterator_
base_types.h:182:43: error: forming reference to void
- 使用带有链表的堆栈数据结构将中缀转换为后缀
- 来自C++结构编程中的中缀表达式的表达式树
- 为什么map有操作符[],set没有
- 中缀后缀转换C ++,似乎无法得到正确的答案
- n-ary布尔语法从中缀到前缀的Boost::Spirit转换?
- 堆栈后缀表示法评估等于中缀计算得到的不同
- 为什么我的前缀中缀代码不显示输出
- 使用 C++ 将中缀转换为后缀(包括赋值运算符)
- 在 c++ 中,为什么 -> 被称为二进制中缀指针成员访问运算符?
- 中缀到带括号的后缀,其中'operands'不是单个标记
- 中缀到后缀 - 优先级不起作用
- 我可以将C 17 Capture lambda ConstexPR转换操作符的结果用作函数指针模板非类型参数吗?
- 中缀到 C++ 中的后缀转换
- c++新操作符通过libstdc++占用大量内存(67MB)
- 使用堆栈的后缀转换器中缀
- 使用单个堆栈的中缀到后缀不适用于大于 9 的数字
- 复制/移动操作符是否可以安全地用于实现复制/移动分配操作符
- 将中缀转换为后缀并获取分段错误核心转储错误
- 在中缀到后缀的输出中没有操作符和括号
- 定义新的中缀操作符