que - estructuras en c++ pdf
Inicializando struct, usando una matriz (7)
Tengo un par de arreglos:
const string a_strs[] = {"cr=1", "ag=2", "gnd=U", "prl=12", "av=123", "sz=345", "rc=6", "pc=12345"};
const string b_strs[] = {"cr=2", "sz=345", "ag=10", "gnd=M", "prl=11", "rc=6", "cp=34", "cv=54", "av=654", "ct=77", "pc=12345"};
que luego necesito analizar para ''='' y luego poner los valores en la estructura. (la clave rc se asigna a la clave fc en la estructura), que tiene la forma de:
struct predict_cache_key {
pck() :
av_id(0),
sz_id(0),
cr_id(0),
cp_id(0),
cv_id(0),
ct_id(0),
fc(0),
gnd(0),
ag(0),
pc(0),
prl_id(0)
{ }
int av_id;
int sz_id;
int cr_id;
int cp_id;
int cv_id;
int ct_id;
int fc;
char gnd;
int ag;
int pc;
long prl_id;
};
El problema que estoy encontrando es que las matrices no están en secuencia o en la misma secuencia que los campos de estructura. Entonces, necesito verificar cada uno y luego presentar un esquema para poner lo mismo en la estructura.
¿Alguna ayuda para usar C o C ++ para resolver lo anterior?
He escrito un pequeño código que te permite inicializar campos, sin tener que preocuparte demasiado sobre si tus campos están fuera de servicio con la inicialización.
Así es como lo usa en su propio código:
/* clients using the above classes derive from lookable_fields */
struct predict_cache_key : private lookable_fields<predict_cache_key> {
predict_cache_key(std::vector<std::string> const& vec) {
for(std::vector<std::string>::const_iterator it = vec.begin();
it != vec.end(); ++it) {
std::size_t i = it->find(''='');
set_member(it->substr(0, i), it->substr(i + 1));
}
}
long get_prl() const {
return prl_id;
}
private:
/* ... and define the members that can be looked up. i''ve only
* implemented int, char and long for this answer. */
BEGIN_FIELDS(predict_cache_key)
FIELD(av_id);
FIELD(sz_id);
FIELD(gnd);
FIELD(prl_id);
END_FIELDS()
int av_id;
int sz_id;
char gnd;
long prl_id;
/* ... */
};
int main() {
std::string const a[] = { "av_id=10", "sz_id=10", "gnd=c",
"prl_id=1192" };
predict_cache_key haha(std::vector<std::string>(a, a + 4));
}
El marco está debajo
template<typename T>
struct entry {
enum type { tchar, tint, tlong } type_name;
/* default ctor, so we can std::map it */
entry() { }
template<typename R>
entry(R (T::*ptr)) {
set_ptr(ptr);
}
void set_ptr(char (T::*ptr)) {
type_name = tchar;
charp = ptr;
};
void set_ptr(int (T::*ptr)) {
type_name = tint;
intp = ptr;
};
void set_ptr(long (T::*ptr)) {
type_name = tlong;
longp = ptr;
};
union {
char (T::*charp);
int (T::*intp);
long (T::*longp);
};
};
#define BEGIN_FIELDS(CLASS) /
friend struct lookable_fields<CLASS>; /
private: /
static void init_fields_() { /
typedef CLASS parent_class;
#define FIELD(X) /
lookable_fields<parent_class>::entry_map[#X].set_ptr(&parent_class::X)
#define END_FIELDS() /
}
template<typename Derived>
struct lookable_fields {
protected:
lookable_fields() {
(void) &initializer; /* instantiate the object */
}
void set_member(std::string const& member, std::string const& value) {
typename entry_map_t::iterator it = entry_map.find(member);
if(it == entry_map.end()) {
std::ostringstream os;
os << "member ''" << member << "'' not found";
throw std::invalid_argument(os.str());
}
Derived * derived = static_cast<Derived*>(this);
std::istringstream ss(value);
switch(it->second.type_name) {
case entry_t::tchar: {
/* convert to char */
ss >> (derived->*it->second.charp);
break;
}
case entry_t::tint: {
/* convert to int */
ss >> (derived->*it->second.intp);
break;
}
case entry_t::tlong: {
/* convert to long */
ss >> (derived->*it->second.longp);
break;
}
}
}
typedef entry<Derived> entry_t;
typedef std::map<std::string, entry_t> entry_map_t;
static entry_map_t entry_map;
private:
struct init_helper {
init_helper() {
Derived::init_fields_();
}
};
/* will call the derived class''s static init function */
static init_helper initializer;
};
template<typename T>
std::map< std::string, entry<T> > lookable_fields<T>::entry_map;
template<typename T>
typename lookable_fields<T>::init_helper lookable_fields<T>::initializer;
Funciona con los datos menos conocidos-member-pointers, que puede tomar de una clase utilizando la sintaxis &classname::member
.
De hecho, como muchos respondieron, existe la necesidad de separar el problema de análisis del problema de construcción del objeto. El patrón Factory es adecuado para eso.
La biblioteca Boost.Spirit también resuelve el problema de análisis sintáctico de manera muy elegante (utiliza la notación EBNF).
Siempre me gusta separar la "lógica de negocios" del código de marco.
Usted puede lograr esto al comenzar a escribir "lo que quiere hacer" de una manera muy conveniente y trabajar a "cómo hacerlo" desde allí.
const CMemberSetter<predict_cache_key>* setters[] =
#define SETTER( tag, type, member ) new TSetter<predict_cache_key,type>( #tag, &predict_cache_key::##member )
{ SETTER( "av", int, av_id )
, SETTER( "sz", int, sz_id )
, SETTER( "cr", int, cr_id )
, SETTER( "cp", int, cp_id )
, SETTER( "cv", int, cv_id )
, SETTER( "ct", int, ct_id )
, SETTER( "fc", int, fc )
, SETTER( "gnd", char, gnd )
, SETTER( "ag", int, ag )
, SETTER( "pc", int, pc )
, SETTER( "prl", long, prl_id )
};
PCKFactory<predict_cache_key> factory ( setters );
predict_cache_key a = factory.factor( a_strs );
predict_cache_key b = factory.factor( b_strs );
Y el marco para lograr esto:
// conversion from key=value pair to "set the value of a member"
// this class merely recognises a key and extracts the value part of the key=value string
//
template< typename BaseClass >
struct CMemberSetter {
const std::string key;
CMemberSetter( const string& aKey ): key( aKey ){}
bool try_set_value( BaseClass& p, const string& key_value ) const {
if( key_value.find( key ) == 0 ) {
size_t value_pos = key_value.find( "=" ) + 1;
action( p, key_value.substr( value_pos ) );
return true;
}
else return false;
}
virtual void action( BaseClass& p, const string& value ) const = 0;
};
// implementation of the action method
//
template< typename BaseClass, typename T >
struct TSetter : public CMemberSetter<BaseClass> {
typedef T BaseClass::*TMember;
TMember member;
TSetter( const string& aKey, const TMember t ): CMemberSetter( aKey ), member(t){}
virtual void action( BaseClass& p, const std::string& valuestring ) const {
// get value
T value ();
stringstream ( valuestring ) >> value;
(p.*member) = value;
}
};
template< typename BaseClass >
struct PCKFactory {
std::vector<const CMemberSetter<BaseClass>*> aSetters;
template< size_t N >
PCKFactory( const CMemberSetter<BaseClass>* (&setters)[N] )
: aSetters( setters, setters+N ) {}
template< size_t N >
BaseClass factor( const string (&key_value_pairs) [N] ) const {
BaseClass pck;
// process each key=value pair
for( const string* pair = key_value_pairs; pair != key_value_pairs + _countof( key_value_pairs); ++pair )
{
std::vector<const CMemberSetter<BaseClass>*>::const_iterator itSetter = aSetters.begin();
while( itSetter != aSetters.end() ) { // optimalization possible
if( (*itSetter)->try_set_value( pck, *pair ) )
break;
++itSetter;
}
}
return pck;
}
};
El problema es que no tienes la metainformación para referirte a los elementos de la estructura en tiempo de ejecución (Algo como structVar. $ ElementName = ..., donde $ ElementName no es el nombre del elemento sino una variable (char?) Que contiene el nombre del elemento que debería ser usado). Mi solución sería agregar esta metainformación. Esto debería ser una matriz con el desplazamiento de los elementos en la estructura.
Solución Quick-n-Dirty: agrega una matriz con las cadenas, el código resultante debería verse así:
const char * wordlist[] = {"pc","gnd","ag","prl_id","fc"};
const int offsets[] = { offsetof(mystruct, pc), offsetof(mystruct, gnd), offsetof(mystruct, ag), offsetof(mystruct, prl_id), offsetof(mystruct, fc)};
const int sizes[] = { sizeof(mystruct.pc), sizeof(mystruct.gnd), sizeof(mystruct.ag), sizeof(mystruct.prl_id), sizeof(mystruct.fc)}
para ingresar algo, entonces harías algo como esto:
index = 0;
while (strcmp(wordlist[index], key) && index < 5)
index++;
if (index <5)
memcpy(&mystructvar + offsets[index], &value, sizes[index]);
else
fprintf(stderr, "Key not valid/n");
Este bucle para las inserciones puede ser costoso si tiene estructuras más grandes, pero C doenst permite la indexación de matriz con cadenas. Pero la informática encontró una solución para este problema: hashes perfectos.
Por lo tanto, después se vería así:
hash=calc_perf_hash(key);
memcpy(&mystruct + offsets[hash], &value, sizes[hash]);
Pero, ¿cómo obtener estas funciones hash perfectas (lo llamé calc_perf_hash)? Existen algoritmos para ello en los que simplemente rellena sus palabras clave, y las funciones aparecen, y afortunadamente alguien incluso las programó: busque la herramienta / paquete "gperf" en su sistema operativo faviourite / distribución. Allí solo ingresaría los 6 nombres de elemento y le daría el código C listo para usar para una función hash perfecta (en genera por defecto una función "hash" que retorna el hash, y una función "in_word_set" que decide si un dado la clave está en la lista de palabras). Debido a que el hash está en orden diferente, tiene por supuesto iniciar las matrices offsetof y size en el orden de los hash.
Otro problema que tiene (y que las demás respuestas no tiene en cuenta) es la conversión de tipo. Los otros hacen una tarea, tengo (no mejor) memcopy. Aquí le sugiero que cambie la matriz de tamaños en otra matriz:
const char * modifier[]={"%i","%c", ...
Donde cada cadena describe el modificador sscanf para leerlo. De esta forma puede reemplazar la asignación / copia por
sscanf(valueString, modifier[hash], &mystructVar + offsets(hash));
Por supuesto, puede variar aquí, incluyendo el "elemento =" en la cadena o similar. Así que puedes poner la cadena completa en valor y no tienes que preprocesarla, creo que esto depende en gran medida del resto de la rutina de análisis.
Esto no debería ser demasiado difícil. El primer problema es que no tiene una matriz de tamaño fijo, por lo que tendría que pasar el tamaño de la matriz, o lo que prefiero es que haga que las matrices terminen en NULL, por ejemplo
const string a_strs[] = {"cr=1", "ag=2", "gnd=U", NULL};
Luego escribiría una función auxiliar (privada) que analizaría la cadena:
bool
parse_string(const string &str, char *buffer, size_t b_size, int *num)
{
char *ptr;
strncpy(buffer, str.c_str(), b_size);
buffer[b_size - 1] = 0;
/* find the ''='' */
ptr = strchr(buffer, ''='');
if (!ptr) return false;
*ptr = ''/0'';
ptr++;
*num = atoi(ptr);
return true;
}
entonces puedes hacer lo que qrdl sugirió.
en un simple bucle for:
for (const string *cur_str = array; *cur_str; cur_str++)
{
char key[128];
int value = 0;
if (!parse_string(*cur_string, key, sizeof(key), &value)
continue;
/* and here what qrdl suggested */
if (!strcmp(key, "cr")) cr_id = value;
else if ...
}
EDITAR: probablemente deberías usar long en lugar de int y atol en lugar de atoi, porque tu prl_id es del tipo long. En segundo lugar, si puede haber números formateados incorrectos después del ''='', debe usar strtol, que puede detectar errores.
Probablemente no lo entendí correctamente, pero las soluciones obvias son dividir cada elemento de la matriz en key
y value
y luego escribir lo-o-ong if-else-if-else ...
secuencia como
if (!strcmp(key, "cr"))
my_struct.cr = value;
else if (!strcmp(key, "ag"))
my_struct.ag = value;
...
Puede automatizar la creación de dicha secuencia con la ayuda del preprocesador C, por ejemplo
#define PROC_KEY_VALUE_PAIR(A) else if (!strcmp(key,#A)) my_struct.##A = value
Por alguna else
razón, escribe el código de esta manera:
if (0);
PROC_KEY_VALUE_PAIR(cr);
PROC_KEY_VALUE_PAIR(ag);
...
El único problema que algunos de los campos de estructura tienen _id
sufijo - para ellos necesitarías crear una macro diferente que _id
sufijo
Si hiciera esto en línea recta, no usaría la madre de todos si. En cambio, haría algo como esto:
typedef struct {
const char *fieldName;
int structOffset;
int fieldSize;
} t_fieldDef;
typedef struct {
int fieldCount;
t_fieldDef *defs;
} t_structLayout;
t_memberDef *GetFieldDefByName(const char *name, t_structLayout *layout)
{
t_fieldDef *defs = layout->defs;
int count = layout->fieldCount;
for (int i=0; i < count; i++) {
if (strcmp(name, defs->fieldName) == 0)
return defs;
defs++;
}
return NULL;
}
/* meta-circular usage */
static t_fieldDef metaFieldDefs[] = {
{ "fieldName", offsetof(t_fieldDef, fieldName), sizeof(const char *) },
{ "structOffset", offsetof(t_fieldDef, structOffset), sizeof(int) },
{ "fieldSize", offsetof(t_fieldDef, fieldSize), sizeof(int) }
};
static t_structLayout metaFieldDefLayout =
{ sizeof(metaFieldDefs) / sizeof(t_fieldDef), metaFieldDefs };
Esto le permite buscar el campo por nombre en tiempo de ejecución con una colección compacta del diseño de la estructura. Esto es bastante fácil de mantener, pero no me gusta el sizeof(mumble)
en el código de uso real, que requiere que todas las definiciones de estructura sean etiquetadas con comentarios que digan: "no cambie los tipos o contenido sin cambiarlos en la matriz t_fieldDef
para esta estructura ". También debe haber una comprobación NULL
.
También preferiría que la búsqueda sea búsqueda binaria o hash, pero esto es probablemente lo suficientemente bueno para la mayoría de los casos. Si t_structLayout
que hacer hash, pondría un puntero a una tabla hash NULL
en el t_structLayout
y en la primera búsqueda, crea el hash.
intenté tu idea y obtuve un
error: ISO C++ forbids declaration of ‘map’ with no type
en Linux ubuntu eclipse cdt.
Deseo notificar que uno debe incluir <map>
en el archivo "* .h" para poder usar su código sin este mensaje de error.
#include <map>
// a framework
template<typename T>
struct entry {
enum type { tchar, tint, tlong } type_name;
/* default ctor, so we can std::map it */
entry() { }
template<typename R>
entry(R (T::*ptr)) {
etcétera etcétera''......