c++ c syntax metaprogramming syntactic-sugar

Azúcar sintáctico en C/C++



syntax metaprogramming (9)

He estado buscando en Ruby y encuentro sus palabras clave "hasta" y "a menos que" muy interesantes. Así que pensé qué era una buena manera de agregar palabras clave similares en C / C ++. Esto es lo que se me ocurrió:

#define until(x) while(!(x)) #define unless(x) if(!(x))

Estoy buscando algunas sugerencias sobre esto. ¿Alguien puede sugerir una mejor alternativa?

Aquí hay un ejemplo de un programa que escribí para ilustrar lo que tenía la intención de hacer:

#include <stdio.h> #include <stdlib.h> #define until(x) while(!(x)) #define unless(x) if(!(x)) unsigned int factorial(unsigned int n) { unsigned int fact=1, i; until ( n==0 ) fact *= n--; return fact; } int main(int argc, char*argv[]) { unless (argc==2) puts("Usage: fact <num>"); else { int n = atoi(argv[1]); if (n<0) puts("please give +ve number"); else printf("factorial(%u) = %u/n",n,factorial(n)); } return 0; }

Sería genial si pudieras indicarme algunas referencias para trucos similares que se pueden emplear en C o C ++.


¿Alguien puede sugerir una mejor alternativa?

Sí. No hagas esto en absoluto. Simplemente use las instrucciones while y if directamente.

Cuando estás programando en C o C ++, programa en C o C ++. Mientras que until y a unless que se usen con frecuencia e idiomáticos en algunos idiomas, no están en C o C ++.


Buen ejemplo de sintaxis de azúcar (en mi humilde opinión):

struct Foo { void bar() {} }; typedef std::vector<Foo*> FooVector; typedef boost::ptr_vector<Foo> FooPtrVector; FooVector v1; for (FooVector::iterator it = v1.begin(); it != v1.end(); ++it) (*it)->bar(); // ugly FooPtrVector v2; for (FooPtrVector::iterator it = v2.begin(); it != v2.end(); ++it) it->bar(); // nice


Como dijo la gente, agregar esas palabras realmente no ofrece un azúcar sintáctico útil, porque el costo de leer un rato (o un if (! Es pequeño, todos los desarrolladores de C están acostumbrados) y al usar esa macro asustará a la mayoría de los desarrolladores). Desarrolladores C. También, hacer que un lenguaje se parezca a otro no es una buena idea.

PERO, el azúcar sintáctico importa. Como ya se dijo, en C ++, boost add lot''s de azúcar sintáctico a través de plantillas, y el stl también proporciona azúcar Somme (por ejemplo, std::make_pair(a, b) es un azúcar sintáctico para std::pair<decltype(a), decltype(b)>(a, b) .

A medida que mejora el lenguaje, se agregan funcionalidades y azúcar sintáctica para mejorar la legibilidad, capacidad de escritura y eficiencia de los desarrolladores. Por ejemplo, con la especificación C ++ 11, se agregó "for (elements in datastructure)" (ver abajo), y también la palabra clave "auto" que permite una inferencia semanal de tipos (digo débil porque necesita escribir muchos tipos en muchos lugares donde el tipo es realmente "obvio" y redundante).

Además, en haskell, usar mónadas sin la notación do (azúcar sintáctico) sería un verdadero dolor, y nadie las usaría 1 .

Un ejemplo sin azúcar sintáctica:

//C++ < 11 std::vector<int> v; v.push_back(3); v.push_back(7); v.push_back(9); v.push_back(12); for (std::vector<int>::iterator it = v.begin(); it != v.end(); it++) { std::cout << *it << std::endl; }

Y con azúcar sintáctico:

//C++ >= 11 std::vector<int> v {3, 7, 9, 12}; for (auto elm : v) { std::cout << elm << std::endl; }

Un poco más legible, ¿no?

Un ejemplo de Haskell para la mónada IO (de 1 ):

f :: IO String f = ask "What''s your name ?" >>= /name -> putStrLn "Write something." >>= /_ -> getLine >>= /string -> putStrLn ("Hello " ++ name ++ " you wrote " ++ string) >>= /_ -> return name g :: IO String g = do name <- ask "What''s your name ?" putStrLn "Write something." string <- getLine putStrLn ("Hello " ++ name ++ " you wrote " ++ string) return name

Aquí hay un enlace a ideone: http://ideone.com/v9BqiZ

1 : En realidad, el lenguaje es más flexible que C ++ y permite la creación de operadores (por ejemplo & ^, +.,: + :, ...), por lo que podríamos imaginar que alguien vuelva a introducir rápidamente el azúcar sintáctico :).


Esto me recordó algo que he visto en el código de alguien:

#define R return;

Además, al hacer que el código sea difícil de comprender, aumenta los costos de mantenimiento.


La forma en que lo hiciste me parece la forma correcta de hacerlo, si es que vas a hacerlo. Debido a que la expansión de la macro es muy similar a lo que cabría esperar [1], creo que es válido hacer que la macro se vea como sintaxis (), en lugar de la SCARY_UPPERCASE_MACROS () que se suele recomendar y que se usa para mostrar que este código no funciona. Sigue la sintaxis habitual y solo debes usarla con cuidado.

[1] El único defecto es la incapacidad para declarar variables, que de todos modos es improbable, y es probable que produzca un error en el lugar correcto cuando se usa incorrectamente, en lugar de hacer algo extraño.

Además, incluso pequeños aumentos en la legibilidad son importantes, por lo que poder decir until ( lugar de while (! realmente hace que sea más fácil leer muchos bucles. Si la condición final es más fácil de considerar como una condición excepcional (independientemente de si es o no) escribir el bucle de esa manera hace que sea más fácil de leer. Por lo tanto, aunque solo sea azúcar sintáctica, creo que hay razones para considerarlo.

Sin embargo no creo que valga la pena. El beneficio es pequeño, ya que la mayoría de los programadores están acostumbrados a leer if (! Y el costo es real: cualquiera que lea el código tendrá que verificar si se trata de una macro o un compilador personalizado, y si hace o no lo que piensa. Y puede inducir a error a pensar que puede hacer cosas como i=5 unless xxxx; Estas pequeñas mejoras, si se generalizan, fragmentarían el lenguaje, por lo que a menudo es mejor hacer las cosas de la manera estándar y adoptar mejoras lentamente.

Sin embargo, se puede hacer bien: la totalidad de boost y tr1, especialmente el material hecho con plantillas para parecerse a extensiones de la biblioteca, implica extender C ++ de varias maneras, muchas de las cuales no se adoptan ya que no parecen validas. pero muchos de ellos tienen un uso pequeño o muy extendido porque hicieron mejoras reales.


Mira cómo se hace impulsar foreach.

El encabezado define BOOST_FOREACH (la fea macro con prefijo). Usted puede

#define foreach BOOST_FOREACH

en tus archivos .cpp para tener un código más limpio. Sin embargo, no debes hacerlo en tus archivos .h y usar el feo BOOST_FOREACH en su lugar.

Ahora, aquí hay un conjunto de macros "funcional-programming-ish" para expresiones "if" (convenientes) if THEN ELSE (porque?: Es feo):

#define IF(x) (x) ? #define ELSE :

ahora

int x = IF(y==0) 1 ELSE IF(y<0) 2*y ELSE 3*y;

Desugarises en:

int x = (y==0) ? 1 : (y<0) ? 2*y : 3*y;


No creo que sus macros sean malas en particular si se usan solo en su propia base de códigos. Este artículo puede ser interesante para ti. Dicho esto, veo algunas desventajas en sus macros cuando las usamos en C ++.
Por ejemplo, no podemos escribir como:

until (T* p = f(x)) ... unless (T* p = f(x)) ...

Por otro lado, podemos escribir como:

while (T* p = f(x)) ... if (T* p = f(x)) ...

En cuanto a unless , si lo definimos como:

#define unless(x) if (x) {} else

entonces podemos escribir a unless (T* p = f(x)) ... Sin embargo, en este caso no podemos agregar la cláusula else después de esto.


Si va a definir macros, es una buena práctica hacer que se vean realmente feas. En particular, deberían ser mayúsculas y tener algún tipo de prefijo. Esto se debe a que no hay espacios de nombres ni coordinación con el sistema de tipos o la resolución de sobrecarga de C ++.

Entonces, si tu macro se llamara BIGYAN_UNNECESSARY_MACRO_UNTIL entonces no estaría del todo "más allá de la palidez".

Si desea extender C ++ con nuevas construcciones en bucle, considere la posibilidad de investigar lambdas en C ++ 0x, donde podría permitir:

until([&] { return finished; }, [&] { // do stuff });

No es perfecto, pero es mejor que las macros.


Sugiero que sería mejor no usarlos.

No puedes usarlos en estilo Ruby como

`printf("hello,world") unless(a>0);`

es ilegal.

Y sería más difícil para los programadores C entender el código. Mientras tanto, la macro extra podría ser un problema.