c++ - prefijo - Definiendo nuevos operadores de infijo.
notacion postfija (1)
Es difícil ver cuál es la pregunta que se hace aquí, asumiendo que la última edición tiene todas las preguntas.
Debería hacerlo para que, en lugar de operadores de infijo, existan operadores de postfix, como "String literal" s.contains "Otro string literal" s, o lo haga con estilo, "String literal" s.contains ("Otro string literal" s)?
Sí. "String literal"s.contains("Other string literal"s)
es la mejor forma: concisa, clara para los programadores de C ++, clara para los programadores de otros lenguajes (las cadenas de Java y Python tienen métodos) y no se utiliza magia de plantilla ni magia de macro .
¿Cómo mejoraría mi código para hacerlo más extensible? Como está ahora, está muy contaminado. ¿Hay una manera mejor / más generalizada / menos torpe de hacer esto? Por ejemplo, para generalizar las expresiones de modo que no necesite definir sentencias o reutilizar el código.
¡Sí! Pero solo hasta cierto punto (eliminé las consts innecesarias de allá y de aquí):
#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
Y luego puedes crear tu operador de infijo así:
#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";
}
Tenga en cuenta que no hay forma de evitar que #define contiene la directiva, ya que no hay manera de crear una directiva macro con otra directiva macro.
¿Cuáles son los beneficios prácticos de esto si hay alguno? (Ignorando toda racionalidad de usar esto como un código del mundo real. Quiero decir, ¿qué puedes sacar de eso por lo que lo uso, a menos que sea con fines recreativos?) Dile que mi amigo , en lugar de aprender C ++, desea una interfaz fácil y abstracta para su experiencia con Bash o Perl, pero le gustaría colaborar sin tener que recurrir a compilar / enlazar fuera de gcc. De esa manera, puede escribir ''scripts'' o ''código'' que es C ++, y compilarlos y vincularlos con mis programas / bibliotecas / interfaz, lo que sea.
Parece que estás intentando crear un lenguaje encima de otro idioma. Prepararse para
- Horas y horas intentando probar tu idioma.
- Vergonzosamente malos mensajes de diagnóstico. Intente compilar esto:
std::vector<void> myarr;
1 Luego envuélvelo con macros. Y luego envolverlo en otra plantilla. Y luego en otras macros ... Entiendes la idea. - Herramientas de depuración mostrando código procesado.
- Incluso si su idioma se integra perfectamente consigo mismo, todavía tiene que cuidar C ++, con un montón de reglas y un sistema de tipo complicado. Después de todo, todas las abstracciones son fugas.
Si tu amigo quiere programar en Perl, solo déjalo hacerlo. Estos lenguajes son fáciles de interactuar con C.
Si está intentando crear un idioma, porque los otros idiomas no pueden expresar claramente lo que está tratando de hacer, los generadores de análisis (Flex / Bison, ANTLR) y LLVM lo hacen fácil.
Si crear un analizador es excesivo, eche un vistazo a los mixins en lenguaje D. Aceptan una cadena creada en el momento de la compilación y luego la compilan como si se hubiera insertado directamente.
Aquí...
import std.stdio;
int main()
{
mixin(`write("Hello world");`); //`contents` is a raw string literal
return 0; //so is r"contents"
}
es equivalente a:
import std.stdio;
int main()
{
write("Hello world");
return 0;
}
Este es solo un ejemplo simple. Podría tener su función que analiza una cadena:
mixin(user1508519s_language(r"(x in a) perform cube"));
1 - Así es como se ve (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
Así que gracias a C ++ 11, ahora es posible combinar macros, literales definidos por el usuario, lambdas, etc. para crear lo más cerca que pueda de "azúcar sintáctica". Un ejemplo sería
if (A contains B)
Por supuesto esto es fácil.
cout <<("hello"_s contains "ello"_s)<<endl;
La expresión se convierte en bool, donde contiene es una estructura personalizada que toma el lado izquierdo y el lado derecho como argumentos. La estructura, por supuesto, sobrecarga al operador + para tomar primero el literal de cadena personalizado, devolviéndose a sí mismo, luego el operador + para la estructura en sí.
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+
Ahora decidí que quiero ir más lejos. Qué pasa
(x in a) perform cube
No es una lista de comprensión, pero es un buen ejemplo, ¿verdad? Al principio dije, bueno, tendría que ir a stackoverflow para preguntar sobre la prioridad del operador personalizado, pero es sencillo ponerlo entre paréntesis ya que nadie en su sano juicio usaría mi código. En lugar de eso, expandí mi otro ejemplo y tengo ''in'' y ''perform'' como estructuras personalizadas, al igual que ''contiene''.
Puede ir más allá y crear una plantilla para que x pueda ser cualquier índice numérico, y como un contenedor, pero por simplicidad, dejé x como un número entero y como un vector de puntos. Hasta ahora, en realidad no toma la variable local x como un argumento, la usa localmente en la función string () del operador.
Para simplificar las cosas, almaceno los resultados de la expresión en una cadena, como así
operator string() const {
string s = "";
for (int x : lhs.rhs)
s += to_string(rhs(x)) + string("/n");
return s;
}
Gracias a otra pregunta: Operador de asignación de sobrecarga para la deducción de tipo
Me di cuenta de que un uso práctico para devolverlo como una tarea es el siguiente:
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;
Gracias a la respuesta de milleniumbug , puedo hacer:
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() +
Hay dos advertencias. Primero, devuelvo un int para poder asignarlo a una variable ficticia para que se ejecute. Siempre podría hacer lo que hacía antes, lo que hice antes, o devolver un objeto std :: function para llamarlo por sí mismo, pero me estaría repitiendo. La otra advertencia es que debido a que hay muchas constantes en la macro, no puede modificar el lhs (que no le permite especificar un iterador).
Todas las cosas consideradas, las siguientes obras como se espera.
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;
Nueva versión
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;
Debería hacerlo para que, en lugar de operadores de infijo, existan operadores de postfix, como "String literal"s.contains "Other string literal"s
, o lo haga con estilo, "String literal"s.contains("Other string literal"s)
?
¿Cómo mejoraría mi código para hacerlo más extensible? Como está ahora, está muy contaminado. ¿Hay una manera mejor / más generalizada / menos torpe de hacer esto? Por ejemplo, para generalizar las expresiones de modo que no necesite definir sentencias o reutilizar el código.