定义新的中缀运算符

时间:2013-03-26 08:10:41

标签: c++ syntactic-sugar

因此,感谢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)

如何改进我的代码以使其更具可扩展性?就像现在一样,它受到严重污染。有没有更好/更普遍/更少笨重的方式来做到这一点?例如,要概括表达式,以便我不需要定义语句或重用代码。

1 个答案:

答案 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 ++的“脚本”或“代码”,并编译和编译   将它与我的程序/库/接口链接,无论如何。

您似乎正在尝试在另一种语言之上创建一种语言。准备

  • 尝试测试语言的时间和小时数。
  • 令人尴尬的错误诊断消息。尝试编译:{{1​​}} 1 然后用宏包装它。然后将其包装在另一个模板中。然后在另一个宏......你明白了。
  • 显示已处理代码的调试工具。
  • 即使你的语言与自身完美融合,你仍然需要C ++来处理,有大量的规则和复杂的类型系统。 After all, all abstractions are leaky.

如果你的朋友想要用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"));