2016-05-18 32 views
5

UPDATE: 6 mesi più tardi, e ho appena imbattuto in questa risposta: Is it legal to index into a struct?: Answer by Slava. Penso che questa sia una soluzione MOLTO migliore di quella fornita qui, poiché non c'è assolutamente alcun comportamento indefinito. Spero che questo aiuti la prossima persona, poiché è già troppo tardi per me da implementare.Scorrere struct per nome variabile


Prima di commentare dicendo di utilizzare un array o un vettore, o qualsiasi forma di contenitore, invece, è una verità dura che non posso. Lo so, questo sarebbe risolto con un array, e ogni soluzione altrimenti è piuttosto "hacky". Mi piacerebbe usare un contenitore, ma assolutamente non posso.

Sono uno sviluppatore di medio livello presso una società molto grande e stiamo utilizzando una libreria aziendale per inviare dati su Ethernet. Ci sono vari motivi per cui non può supportare matrici/vettori e, invece, utilizza le strutture di POD (Plain Old Data: char, float, ints, bool). Comincio con una serie di float che devo usare per riempire una struttura con lo stesso numero di float. Poiché lo scopo di questa libreria è di inviare messaggi su Ethernet, ho solo bisogno di fare l'iterazione due volte - una volta sull'invio e una sulla ricezione. Tutte le altre volte, questi dati vengono archiviati come una matrice. Lo so - dovrei serializzare gli array e inviarli così com'è, ma lo ripeto - assolutamente non posso.

Ho un float[1024], e deve scorrere l'array e compilare il seguente struct:

struct pseudovector 
{ 
    float data1; 
    float data2; 
    float data3; 
    ... 
    float data1024; 
} 

sto già generando questa struct con BOOST_PP_REPEAT e BOOST_PP_SEQ_FOR_EACH_I in modo che io non devo scrivere tutte le 1024 carri e aumenta la manutenibilità/estensibilità.

Allo stesso modo, ho provato scorrendo la struct tramite pre-compilatore ## concatination (https://stackoverflow.com/a/29020943/2066079), ma come questo è fatto in fase di pre-compilatore, non può essere utilizzato per runtime ottenere/impostazione.

Ho esaminato l'implementazione di reflection come How can I add reflection to a C++ application? e Ponder Library, ma entrambi gli approcci richiedono di scrivere esplicitamente ciascun elemento su cui è possibile riflettere. In quel caso, potrei anche solo creare un std::map<string, float> e iterare in un ciclo for tramite stringa/integer concatenazione:

for(i=0;i<1024;i++) 
{ 
    array[i] = map.get(std::string("data")+(i+1)) 
} 

Qualcuno può consigliare una soluzione più pulita che non mi richiede di scrivere al di sopra di 1024 linee di codice? Il tuo aiuto è apprezzato!

Di nuovo, ripeto - Non posso assolutamente utilizzare una matrice/vettore di qualsiasi tipo.

+0

questo sembra anti-pattern. potresti avvolgere questa struttura in un sindacato? 'union wrapper {float arr [1024]; pseudovettore vec; }; ' – vu1p3n0x

+3

Perché non solo ' float [1024] fa; pseudovettore pv; memcpy (fa, & pv, sizeof (pv)); '? –

+0

@ c-smile Perché si tratta di comportamento non definito – dberm22

risposta

8

Questo potrebbe essere più facile di quanto ci si aspetta. Innanzitutto, alcuni avvertimenti:

  1. Gli array sono garantiti, dallo standard, contigui; vale a dire, non è inserito alcun padding tra loro e la matrice stessa è allineata con i requisiti di allineamento del tipo di elemento.

  2. Structs non hanno tali restrizioni; possono essere soggetti a imbottiture arbitrarie. Tuttavia, una determinata implementazione (in una determinata versione) lo farà allo stesso modo in tutte le unità di traduzione (altrimenti, in quale altro modo si potrebbe utilizzare la stessa definizione di struttura per passare i dati tra le unità di traduzione?).Il modo normale in cui viene eseguita è abbastanza ragionevole, specialmente quando la struttura contiene solo membri di un singolo tipo. Per una tale struttura, l'allineamento di solito corrisponde all'allineamento più grande dei membri, e c'è in genere senza riempimento perché tutti i membri hanno lo stesso allineamento.

Nel tuo caso, la matrice di 1024 carri allegorici e la vostra struct con 1024 membri galleggiante quasi certamente hanno esattamente lo stesso layout di memoria. Questo è assolutamente non garantita dallo standard, ma il compilatore può documentare le sue regole di layout struct, e si può sempre affermare il dimensioni e allineamenti partita nella tua unit test (si dispone di test di unità, giusto?)

Dato quelle avvertenze, sarà quasi certamente essere in grado di semplicemente reinterpret_cast (o memcpy) tra i due.

2

È possibile utilizzare la punteggiatura dei tipi per trattare la struttura come una matrice.

float array[1024] = { ... }; 
pseudovector pv1; 
float *f = static_cast<float*>(static_cast<void*>(&pv1)); 
for (int i = 0; i < 1024; i++) { 
    f[i] = array[i]; 
} 
+0

Finché si sta bene con un comportamento non definito. –

+0

Dato che sta inviando la struttura su Ethernet, è ovviamente OK con quello. Il codice per scriverlo fa quasi certamente la stessa assunzione di dati contigui. – Barmar

+0

Bello, grazie! E questo funziona perché tutti gli elementi nella struct sono float, giusto? Se ci fossero tipi misti (cioè informazioni di intestazione in ogni struttura) questo non potrebbe essere usato? – dberm22

1

È possibile utilizzare la metaprogrammazione del preprocessore per creare l'array. Potresti fare qualcosa del tipo:

#define ACCESSOR(z, n, type) &type::data ## n 

auto arr[] = { 
    BOOST_PP_ENUM(1000, ACCESSOR, pseudovector) 
}; 

Sarà necessario regolare ACCESSOR molto probabilmente. Potrebbe non essere legale utilizzare auto qui.

poi si fa:

auto val = (pv1.*arr)[4]; 

ecc ...

No UB necessario.

1

Usa Boost.Hana, se si può compilare con le versioni recenti di Clang o GCC (per quanto ne so, gli unici compilatori supportati.)

Permettetemi di citare la sezione Introspection dal tutorial:

introspezione Static , come discuteremo qui, è la capacità di un programma di esaminare il tipo di un oggetto in fase di compilazione. In altre parole , è un'interfaccia programmatica per interagire con i tipi in in fase di compilazione. Ad esempio, hai mai desiderato verificare se qualche tipo sconosciuto di ha un membro chiamato foo? O forse ad un certo punto hai necessario per iterare sui membri di una struct?

Per introspecting user-defined types che c'è bisogno di definire la struct con Hana, ma questo non è molto diversa da quella che definisce una struttura altrimenti:

struct pseudovector { 
    BOOST_HANA_DEFINE_STRUCT(pseudovector, 
    (float, data1), 
    (float, data2), 
    … 
); 
}; 

E si dovrebbe facilmente essere in grado di modificare ciò che le macro si deve genera la tua struttura attuale, per generare invece questa.

Questo aggiunge una struttura nidificata a pseudovector, che consiste solo di una funzione membro statico. Non influenza POD-ness, dimensioni o layout dei dati.

Poi si può scorrere come in questo esempio:

pseudovector pv; 

hana::for_each(pv, [](auto pair) { 
    std::cout << hana::to<char const*>(hana::first(pair)) << ": " 
      << hana::second(pair) << std::endl; 
}); 

Qui, i membri pair s' sono una stringa hana fase di compilazione (il nome del membro) e il valore. Se preferisci che il tuo lambda prenda due argomenti (il nome e il valore), usa hana::fuse:

hana::for_each(pv, hana::fuse([](auto name, auto member) { 
    std::cout << hana::to<char const*>(name) << ": " << member << std::endl; 
})); 
+0

@Andrew: done. FWIW, at il tempo che stavo pensando in termini di dimostrare che Hana fa ciò che si desidera, citando i documenti – Kundor