c++ - smart - solidity español
C++ Equivalente a los inicializadores designados? (6)
Recientemente he estado trabajando en algunos dispositivos integrados, donde tenemos algunas estructuras y uniones que deben inicializarse en el momento de la compilación para que podamos mantener ciertas cosas en flash o ROM que no necesitan ser modificadas, y guardar un poco flash o SRAM a un costo de rendimiento un poco. Actualmente, el código se compila como C99 válido, pero sin este ajuste, también se compilaba como código C ++, y sería genial apoyar las cosas que se compilan de esa manera también. Una de las cosas clave que previene esto es que estamos usando inicializadores designados C99 que no funcionan dentro del subconjunto C de C ++. No soy muy aficionado a C ++, así que me pregunto qué maneras simples podría haber para que esto suceda en C compatible con C ++ o en C ++ que todavía permite la inicialización en tiempo de compilación para que las estructuras y uniones no necesiten ser inicializado después del inicio del programa en SRAM.
Un punto adicional de la nota: una razón clave para el uso del inicializador designado es INICIARSE como NO el primer miembro de una unión. Además, seguir con el estándar C ++ o ANSI C es una ventaja para mantener la compatibilidad con otros compiladores (conozco las extensiones de GNU que proporcionan algo así como los inicializadores designados sin C99).
Basándose en la respuesta de Shing Yip, y con el beneficio de 3 años, C ++ 11 ahora puede garantizar la inicialización del tiempo de compilación:
union Bar
{
constexpr Bar(int a) : a_(a) {}
constexpr Bar(float b) : b_(b) {}
int a_;
float b_;
};
constexpr Bar bar1(1);
constexpr Bar bar2(1.234f);
El siguiente código se compila sin problemas con g ++:
#include <iostream>
struct foo
{
int a;
int b;
int c;
};
union bar
{
int a;
float b;
long c;
};
static foo s_foo1 = {1,2,3};
static foo s_foo2 = {1,2};
static bar s_bar1 = {42L};
static bar s_bar2 = {1078523331}; // 3.14 in float
int main(int, char**)
{
std::cout << s_foo1.a << ", " <<
s_foo1.b << ", " <<
s_foo1.c << std::endl;
std::cout << s_foo2.a << ", " <<
s_foo2.b << ", " <<
s_foo2.c << std::endl;
std::cout << s_bar1.a << ", " <<
s_bar1.b << ", " <<
s_bar1.c << std::endl;
std::cout << s_bar2.a << ", " <<
s_bar2.b << ", " <<
s_bar2.c << std::endl;
return 0;
}
Este es el resultado:
$ g++ -o ./test ./test.cpp
$ ./test
1, 2, 3
1, 2, 0
42, 5.88545e-44, 42
1078523331, 3.14, 1078523331
Lo único con los inicializadores de C ++ es que necesita inicializar todos los elementos de la estructura o el resto se inicializará con ceros. No puedes elegir. Pero eso aún debería estar bien para su caso de uso.
Un punto adicional de la nota: una razón clave para el uso del inicializador designado es INICIARSE como NO el primer miembro de una unión.
Para eso necesita usar la "solución alternativa" que se muestra en el ejemplo donde configuro el miembro "flotante" proporcionando el valor int equivalente. Es un poco complicado, pero si resuelve tu problema.
Esto es una especie de respuesta y pregunta. Me doy cuenta de que este hilo está muerto, pero es exactamente lo que estaba buscando esta noche.
Hice un poco de hurgar y lo más cercano que puedo llegar a lo que quiero (que es similar a lo que quieres ... He estado trabajando con fotos y no necesito usar c ++, pero tengo curiosidad de cómo podría hacerse ) es el primer ejemplo de código:
#include <iostream>
using namespace std;
extern "C"
{
typedef struct stuff
{
int x;
double y;
} things;
}
int main()
{
things jmcd = { jmcd.x = 12, jmcd.y = 10.1234 };
cout << jmcd.x << " " << jmcd.y << endl;
return 0;
}
Esto tiene un aspecto muy similar a los inicializadores designados de estilo C99 con una advertencia que mencionaré más adelante. (Probablemente envolverías esto en #ifdef __cplusplus si quisieras que compilara la estructura.) La segunda versión del código que miré es esta:
#include <iostream>
using namespace std;
extern "C"
{
typedef struct stuff
{
int x;
double y;
} things;
}
int main()
{
things jmcd;
jmcd.x = 12;
jmcd.y = 10.1234;
cout << jmcd.x << " " << jmcd.y << endl;
return 0;
}
Básicamente, desde el desmontaje, parece que el primer ejemplo es realmente más lento. Miré la salida de la asamblea y, bueno, debo estar un poco oxidado. Tal vez alguien podría darme una idea. La salida de ensamblaje de la primera cpp compilada y se veía así:
main:
.LFB957:
.cfi_startproc
.cfi_personality 0x0,__gxx_personality_v0
pushl %ebp
.cfi_def_cfa_offset 8
movl %esp, %ebp
.cfi_offset 5, -8
.cfi_def_cfa_register 5
subl $24, %esp
movl $0, 12(%esp)
movl $0, 16(%esp)
movl $0, 20(%esp)
movl $12, 12(%esp)
movl 12(%esp), %eax
movl %eax, 12(%esp)
fldl .LC0
fstpl 16(%esp)
fldl 16(%esp)
fstpl 16(%esp)
movl 12(%esp), %eax
movl %eax, 4(%esp)
fildl 4(%esp)
fldl 16(%esp)
faddp %st, %st(1)
fnstcw 2(%esp)
movzwl 2(%esp), %eax
movb $12, %ah
movw %ax, (%esp)
fldcw (%esp)
fistpl 4(%esp)
fldcw 2(%esp)
movl 4(%esp), %eax
leave
ret
.cfi_endproc
El segundo ejemplo se veía así:
main:
.LFB957:
.cfi_startproc
.cfi_personality 0x0,__gxx_personality_v0
pushl %ebp
.cfi_def_cfa_offset 8
movl %esp, %ebp
.cfi_offset 5, -8
.cfi_def_cfa_register 5
subl $24, %esp
movl $12, 12(%esp)
fldl .LC0
fstpl 16(%esp)
movl 12(%esp), %eax
movl %eax, 4(%esp)
fildl 4(%esp)
fldl 16(%esp)
faddp %st, %st(1)
fnstcw 2(%esp)
movzwl 2(%esp), %eax
movb $12, %ah
movw %ax, (%esp)
fldcw (%esp)
fistpl 4(%esp)
fldcw 2(%esp)
movl 4(%esp), %eax
leave
ret
.cfi_endproc
Ambos se generaron con un comando g++ -O0 -S main.cpp
. Claramente, el ejemplo intuitivamente menos eficiente generó un código de operación más eficiente en términos de número de instrucciones. Por otro lado, hay pocos casos en los que pueda imaginar que las pocas instrucciones sean críticas. (Por otro lado, realmente tengo problemas para entender el montaje no escrito por humanos, así que tal vez me falta algo ...) Creo que esto proporciona una solución, aunque tarde, a la pregunta que hizo James. Lo próximo que debería probar es si se permite la misma inicialización en C99; si eso funciona, creo que aborda completamente el problema de James.
Descargo de responsabilidad: no tengo idea si esto funciona o se comporta de manera similar para cualquier otro compilador que no sea g ++.
Informe de agujero seco:
Dado
struct S {
int mA;
int mB;
S() {}
S(int b} : mB(b) {} // a ctor that does partial initialization
};
Probé derivando S1 de S, donde el constructor por defecto en línea de S1 invoca S (int) y pasa un valor codificado ...
struct S1 {
S1() : S(22) {}
} s1;
... y luego compilado con gcc 4.0.1 -O2 -S. La esperanza era que el optimizador vería que s1.mB necesariamente sería 22 y le asignaría el valor en tiempo de compilación, pero del ensamblador ...
movl $22, 4+_s1-"L00000000002$pb"(%ebx)
... parece que el código generado hace la inicialización en tiempo de ejecución antes de main. Incluso si hubiera funcionado, difícilmente podría ser compilable como C99 y tendría el riesgo de derivar una clase para cada objeto que quisiera inicializar; entonces, no te molestes
No estoy seguro de que puedas hacerlo en C ++. Para las cosas que necesita inicializar utilizando los inicializadores designados, puede colocarlos por separado en un archivo .c
compilado como C99, por ejemplo:
// In common header file
typedef union my_union
{
int i;
float f;
} my_union;
extern const my_union g_var;
// In file compiled as C99
const my_union g_var = { .f = 3.14159f };
// Now any file that #include''s the header can access g_var, and it will be
// properly initialized at load time
#ifdef __cplusplus
struct Foo
{
Foo(int a, int b) : a(a), b(b) {}
int a;
int b;
};
union Bar
{
Bar(int a) : a(a) {}
Bar(float b) : b(b) {}
int a;
float b;
};
static Foo foo(1,2);
static Bar bar1(1);
static Bar bar2(1.234f);
#else
/* C99 stuff */
#endif // __cplusplus
En C ++, la unión puede tener constructores también. ¿Puede ser que esto es lo que querías?