Так что, благодаря 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)
?
Как бы я улучшил свой код, чтобы сделать его более расширяемым? Как это сейчас, это очень загрязнено. Есть ли лучший / более обобщенный / менее неуклюжий способ сделать это? Например, чтобы обобщить выражения так, чтобы мне не нужно было определять операторы или повторно использовать код.
Трудно понять, что за вопрос задают здесь, учитывая, что в последней редакции есть все вопросы.
Должен ли я сделать так, чтобы вместо инфиксных операторов существовал постфикс
операторы, такие как «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 Затем оберните его макросами. А затем заверните его в другой шаблон. А потом еще в макросах … Вы поняли идею.Если ваш друг хочет программировать на 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
Других решений пока нет …