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