Определение новых инфиксных операторов

Так что, благодаря C ++ 11, теперь можно комбинировать макросы, пользовательские литералы, лямбды и т. Д., Чтобы создать наиболее близкий мне «синтаксический сахар». Примером будет

 if (A contains B)

Конечно это легко.

cout <<("hello"_s contains "ello"_s)<<endl;

Выражение преобразуется в bool, где contains — это пользовательская структура, которая принимает левую и правую части в качестве аргументов. Структура, конечно, перегружает оператор +, чтобы сначала взять пользовательский строковый литерал, возвращая себя, а затем оператор + для самой структуры.

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» и «execute» как пользовательские структуры, как «содержащиеся».

Вы можете пойти дальше и шаблонировать его так, чтобы 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;

Благодаря ответ тысячелетнего жука, Я могу сделать:

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, чтобы вызвать его сам, но я бы повторил это. Другое предостережение заключается в том, что поскольку в макросе так много констант, вы не можете изменять 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)?

Как бы я улучшил свой код, чтобы сделать его более расширяемым? Как это сейчас, это очень загрязнено. Есть ли лучший / более обобщенный / менее неуклюжий способ сделать это? Например, чтобы обобщить выражения так, чтобы мне не нужно было определять операторы или повторно использовать код.

24

Решение

Трудно понять, что за вопрос задают здесь, учитывая, что в последней редакции есть все вопросы.

Должен ли я сделать так, чтобы вместо инфиксных операторов существовал постфикс
операторы, такие как «String literal» s.contains «Другие строковые литералы» s, или
сделать это в функциональном стиле, «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, поскольку нет способа создать директиву macro с другой директивой macro.

Каковы практические преимущества этого, если таковые имеются (игнорируя все
рациональность использования этого в качестве кода реального мира. Я имею в виду, что вы можете получить
из того, для чего я его использую, за исключением развлекательных целей?)
Скажи, что мой друг, вместо того, чтобы изучать 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:\__moje\prog\mingw\bin\../lib/gcc/mingw32/4.7.2/include/
c++/bits/stl_construct.h:63:0,
from c:\__moje\prog\mingw\bin\../lib/gcc/mingw32/4.7.2/include/
c++/vector:63,
from #templateerrors2.cpp:1:
c:\__moje\prog\mingw\bin\../lib/gcc/mingw32/4.7.2/include/c++/ext/alloc_traits.h
: In instantiation of 'struct __gnu_cxx::__alloc_traits<std::allocator<void> >':

c:\__moje\prog\mingw\bin\../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:\__moje\prog\mingw\bin\../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:\__moje\prog\mingw\bin\../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:\__moje\prog\mingw\bin\../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:\__moje\prog\mingw\bin\../lib/gcc/mingw32/4.7.2/include/
c++/vector:65:0,
from #templateerrors2.cpp:1:
c:\__moje\prog\mingw\bin\../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:\__moje\prog\mingw\bin\../lib/gcc/mingw32/4.7.2/include/c++/bits/stl_vector.h:
292:7: error: forming reference to void
c:\__moje\prog\mingw\bin\../lib/gcc/mingw32/4.7.2/include/c++/bits/stl_vector.h:
467:7: error: forming reference to void
c:\__moje\prog\mingw\bin\../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:\__moje\prog\mingw\bin\../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:\__moje\prog\mingw\bin\../lib/gcc/mingw32/4.7.2/include/c++/bits/stl_vector.h:
881:7: error: forming reference to void
In file included from c:\__moje\prog\mingw\bin\../lib/gcc/mingw32/4.7.2/include/
c++/vector:70:0,
from #templateerrors2.cpp:1:
c:\__moje\prog\mingw\bin\../lib/gcc/mingw32/4.7.2/include/c++/bits/vector.tcc:10
8:5: error: forming reference to void
In file included from c:\__moje\prog\mingw\bin\../lib/gcc/mingw32/4.7.2/include/
c++/vector:65:0,
from #templateerrors2.cpp:1:
c:\__moje\prog\mingw\bin\../lib/gcc/mingw32/4.7.2/include/c++/bits/stl_vector.h:
1003:7: error: forming reference to void
c:\__moje\prog\mingw\bin\../lib/gcc/mingw32/4.7.2/include/c++/bits/stl_vector.h:
1179:7: error: forming reference to void
In file included from c:\__moje\prog\mingw\bin\../lib/gcc/mingw32/4.7.2/include/
c++/vector:70:0,
from #templateerrors2.cpp:1:
c:\__moje\prog\mingw\bin\../lib/gcc/mingw32/4.7.2/include/c++/bits/vector.tcc:21
6:5: error: forming reference to void
c:\__moje\prog\mingw\bin\../lib/gcc/mingw32/4.7.2/include/c++/bits/vector.tcc:43
9:5: error: forming reference to void
c:\__moje\prog\mingw\bin\../lib/gcc/mingw32/4.7.2/include/c++/bits/vector.tcc:31
6:5: error: forming reference to void
In file included from c:\__moje\prog\mingw\bin\../lib/gcc/mingw32/4.7.2/include/
c++/vector:65:0,
from #templateerrors2.cpp:1:
c:\__moje\prog\mingw\bin\../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:\__moje\prog\mingw\bin\../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:\__moje\prog\mingw\bin\../lib/gcc/mingw32/4.7.2/include/c++/bits/stl_vector.h:
161:9: error: invalid use of 'void'
c:\__moje\prog\mingw\bin\../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:\__moje\prog\mingw\bin\../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:\__moje\prog\mingw\bin\../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:\__moje\prog\mingw\bin\../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:\__moje\prog\mingw\bin\../lib/gcc/mingw32/4.7.2/include/
c++/bits/stl_algobase.h:66:0,
from c:\__moje\prog\mingw\bin\../lib/gcc/mingw32/4.7.2/include/
c++/vector:61,
from #templateerrors2.cpp:1:
c:\__moje\prog\mingw\bin\../lib/gcc/mingw32/4.7.2/include/c++/bits/stl_iterator_
base_types.h: In instantiation of 'struct std::iterator_traits<void*>':
c:\__moje\prog\mingw\bin\../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:\__moje\prog\mingw\bin\../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:\__moje\prog\mingw\bin\../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:\__moje\prog\mingw\bin\../lib/gcc/mingw32/4.7.2/include/c++/bits/stl_iterator_
base_types.h:182:43: error: forming reference to void
12

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]