Итак, благодаря C ++ 11 теперь можно комбинировать макросы, определяемые пользователем литералы, лямбда-выражения и т. Д. Для создания максимально приближенного к «синтаксическому сахару». Примером может быть
if (A contains B)
Конечно, это легко.
cout <<("hello"_s contains "ello"_s)<<endl;
Выражение преобразуется в логическое значение, где содержит настраиваемую структуру, которая принимает левую и правую части в качестве аргументов. Структура, конечно, перегружает оператор +, чтобы сначала принять настраиваемый строковый литерал, вернув сам себя, а затем оператор + для самой структуры.
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 в качестве аргумента, он использует ее локально в функции operator 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, чтобы я мог назначить его фиктивной переменной, чтобы заставить ее выполнить. Я всегда мог сделать то, что делал раньше, или вернуть объект std :: function, чтобы вызвать его сам по себе, но я бы повторил себя. Другое предостережение заключается в том, что, поскольку в макросе так много констант, вы не можете изменять 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)
?
Как мне улучшить свой код, чтобы сделать его более расширяемым? Сейчас он очень загрязнен. Есть ли лучший / более обобщенный / менее неуклюжий способ сделать это? Например, чтобы обобщить выражения, чтобы мне не нужно было определять операторы или повторно использовать код.