Initializing struct, using an array - c++

I have a couple of array's:
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"};
which i then need to parse out for '=' and then put the values in the struct. (the rc key maps to the fc key in the struct), which is in the form of:
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;
};
The problem I am encountering is that the array's are not in sequence or in the same sequence as the struct fields. So, I need to check each and then come up with a scheme to put the same into the struct.
Any help in using C or C++ to solve the above?

Probably I didn't get it correctly, but obvious solutions is to split each array element into key and value and then write lo-o-ong if-else-if-else ... sequence like
if (!strcmp(key, "cr"))
my_struct.cr = value;
else if (!strcmp(key, "ag"))
my_struct.ag = value;
...
You can automate the creation of such sequence with the help of C preprocessor, e.g.
#define PROC_KEY_VALUE_PAIR(A) else if (!strcmp(key,#A)) my_struct.##A = value
Because of leading else you write the code this way:
if (0);
PROC_KEY_VALUE_PAIR(cr);
PROC_KEY_VALUE_PAIR(ag);
...
The only problem that some of you struct fields have _id sufffix - for them you'd need to create a bit different macro that will paste _id suffix

This shouldn't be too hard. Your first problem is that you don't have a fixed sized array, so you'd have to pass the size of the array, or what I'd prefer you make the arrays NULL-terminated, e.g.
const string a_strs[] = {"cr=1", "ag=2", "gnd=U", NULL};
Then I would write a (private) helper function that parse the string:
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;
}
then you can do what qrdl has suggested.
in a simple for loop:
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 ...
}
EDIT: you should probably use long instead of int and atol instead of atoi, because your prl_id is of the type long. Second if there could be wrong formated numbers after the '=', you should use strtol, which can catch errors.

I've written some little code that allows you to initialize fields, without having to worry too much about whether your fields are going out of order with the initialization.
Here is how you use it in your own code:
/* 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));
}
The framework is below
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;
It works using the lesser known data-member-pointers, which you can take from a class using the syntax &classname::member.

Indeed, like many answered, there is a need to separate the parsing problem from the object construction problem. The Factory pattern is suited well for that.
The Boost.Spirit library also solves the parse->function problem in a very elegant way (uses EBNF notation).
I always like to separate the 'business logic' from the framework code.
You can achieve this by start writing "what you want to do" in a very convenient way and work to "how do you do it" from there.
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 );
And the framework to achieve this:
// 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;
}
};

The problem is you dont have the metainformation to refer to the struct elements at run time (Something like structVar.$ElementName = ..., where $ElementName is not the element name but a (char?)variable containing the element name which should be used).
My solution would be to add this metainformation.
This should be an array with the offset of the elements in the struct.
Quick-n-Dirty solution: you add an array with the strings, the resulting code should look like this:
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)}
to enter something you would then something like this:
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");
This loop for the inserts can get costly if you have bigger structures, but C doenst allow array indexing with strings. But the computer science found a solution for this problem: perfect hashes.
So it would afterwards look like this:
hash=calc_perf_hash(key);
memcpy(&mystruct + offsets[hash], &value, sizes[hash]);
But how to obtain these perfect hash functions (I called it calc_perf_hash)?
There exist algorithms for it where you just stuff your keywords in, and the functions comes out, and luckily someone even programmed them: look for the "gperf" tool/package in your faviourite OS/distribution.
There you would just input the 6 element names and he outputs you the ready to use C code for a perfect hash function (in generates per default a function "hash" which returnes the hash, and an "in_word_set" function which decides if a given key is in the word list).
Because the hash is in different order, you have of course to initilize the offsetof and size arrays in the order of the hashes.
Another problem you have (and which the other answers doesnt take into account) is the type conversion. The others make an assignment, I have (not better) memcopy.
Here I would suggest you change the sizes array into another array:
const char * modifier[]={"%i","%c", ...
Where each string describes the sscanf modifier to read it in. This way you can replace the assignment/copy by
sscanf(valueString, modifier[hash], &mystructVar + offsets(hash));
Cf course you can vary here, by including the "element=" into the string or similar. So you can put the complete string into value and dont have to preprocess it, I think this depends strongly on the rest of you parse routine.

Were I to do this in straight C, I wouldn't use the mother of all if's. Instead, I would do something like this:
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 };
This lets you look up the field by name at runtime with a compact collection of the struct layout. This is fairly easy to maintain, but I don't like the sizeof(mumble) in the actual usage code - that requires that all struct definitions get labeled with comments saying, "don't effing change the types or content without changing them in the t_fieldDef array for this structure". There also needs to be NULL checking.
I'd also prefer that the lookup be either binary search or hash, but this is probably good enough for most cases. If I were to do hash, I'd put a pointer to a NULL hashtable into the t_structLayout and on first search, build the hash.

tried your idea and got an
error: ISO C++ forbids declaration of ‘map’ with no type
in linux ubuntu eclipse cdt.
I wish to notify that one should include <map> in the "*.h" file
in order to use your code without this error message.
#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' etc'......

Related

Class design - use optionals? variants? be opaque?

I want to have a class for PCI bus locations. For the sake of discussion, these come in three forms:
[domain]:[bus]:[device].[function]
[domain]:[bus]:[device]
[bus]:[device].[function]
and let's say each field is a non-negative integral value (let's even say unsigned just to make things simple).
I'm scratching my head regarding how to define this class. I could use std::optionals for the domain and function fields; but then, they're not both optional. I could use a variant with 3 types, but then I need to define separate types, which overlap a lot. I could just hold 4 unsigneds and a 3-value enum for which format is in effect - but that's quite a bit of hassle, and I'd need getter and to make the class opaque. Same thing if I try to use a union somehow.
It seems like every choice I make, it's going to be an iffy class. How can I minimize my displeasure with it?
Note: Any language standard version is ok for the answer, although I doubt C++20 would give you anything.
Building upon my comment, I was wondering if something like this could work:
enum class pci_format { domain_function, domain, function };
template <pci_format E> struct tag { };
class pci_location {
public:
pci_location (tag<pci_format::domain_function>, unsigned domain, unsigned bus,
unsigned device, unsigned function)
: format_(pci_format::domain_function)
, domain_(domain)
, bus_(bus)
, device_(device)
, function_(function)
{ }
// Repeat for other values of pci_format.
pci_format format () const { return format_; }
bool has_domain () const {
return (format_ == pci_format::domain_function)
or (format_ == pci_format::domain);
}
unsigned domain () const {
if (not has_domain()) { throw std::runtime_error("Domain not available."); }
return domain_;
}
// Repeat for other fields.
private:
pci_format format_;
unsigned domain_;
unsigned bus_;
unsigned device_;
unsigned function_
};
You would basically create a specific constructor for each PCI "format". Of course you could also store each unsigned as an std::optional<unsigned>, but that would force users to "dereference" each optional even if they knew for sure that it must contain a value.
One way or another, they'll have to check what "format" the location is in, so it seems to me that using an enum for this is more user friendly. Then users only have to check once and know exactly which fields are available.
I guess you could layer a visitor on top of all this so they can simply provide code to execute for each "format":
struct pci_location_visitor {
virtual void visit (tag<pci_format::domain_function>, pci_location const & obj) = 0;
// Repeat for other enum values.
};
// Add to pci_location:
void accept (pci_location_visitor & visitor) {
switch (format_) {
case pci_format::domain_function:
return visitor.visit(tag<pci_format::domain_function>{}, *this);
default: throw std::runtime_error("Format not supported for visitation.");
}
}
Then on top of that you could create a visitor that can be constructed from a bunch of callables, i.e. lambdas, so that this all can be used like below:
pci_location const & loc = getIt();
auto printSomething = make_pci_location_visitor(
[](tag<pci_format::domain_function>, pci_location const & e) { std::cout << e.domain(); }
, [](tag<pci_format::domain>, pci_location const & e) { std::cout << e.bus(); }
, [](tag<pci_format::function>, pci_location const & e) { std::cout << e.function(); }
);
loc.accept(printSomething);
For an example of how such a visitor could be constructed, see the overloaded class in the std::visit example on cppreference.com.
As requested in comments... given that I have no particular requirements how the users would prefer to use this class, given C++14, I would be doing something generic along the lines of:
#include <array>
#include <climits>
#include <iostream>
#include <stdexcept>
class pci_location_t {
public:
struct dbdf {
unsigned int domain;
unsigned int bus;
unsigned int device;
unsigned int function;
};
struct dbd {
unsigned int domain;
unsigned int bus;
unsigned int device;
};
struct bdf {
unsigned int bus;
unsigned int device;
unsigned int function;
};
pci_location_t(dbdf v) : domain(v.domain), bus(v.bus), device(v.device), function(v.function) {}
pci_location_t(dbd v) : domain(v.domain), bus(v.bus), device(v.device), function(INVALID) {}
pci_location_t(bdf v) : domain(INVALID), bus(v.bus), device(v.device), function(v.function) {}
template <typename dbdf_f, typename dbd_f, typename bdf_f>
auto visit(dbdf_f dbdf_fun, dbd_f dbd_fun, bdf_f bdf_fun) const {
if (domain == INVALID) {
if (function == INVALID) {
throw std::domain_error("Wrong PCI location format");
}
return bdf_fun(bdf{bus, device, function});
} else if (function == INVALID) {
return dbd_fun(dbd{domain, bus, device});
} else {
return dbdf_fun(dbdf{domain, bus, device, function});
}
}
private:
friend pci_location_t invalid_location();
pci_location_t() : domain(INVALID), bus(INVALID), device(INVALID), function(INVALID) {}
const static unsigned int INVALID = UINT_MAX;
unsigned int domain;
unsigned int bus;
unsigned int device;
unsigned int function;
};
pci_location_t invalid_location() { return pci_location_t{}; }
int main() {
std::array<pci_location_t, 4> locations = {
pci_location_t(pci_location_t::dbdf{1, 2, 3, 4}),
pci_location_t(pci_location_t::dbd{1, 2, 3}),
pci_location_t(pci_location_t::bdf{2, 3, 4}),
invalid_location()
};
try {
for (auto& l : locations) {
l.visit(
[] (auto dbdf) {
std::cout << dbdf.domain << ":" << dbdf.bus << ":" << dbdf.device << "." << dbdf.function << std::endl;
},
[] (auto dbd) {
std::cout << dbd.domain << ":" << dbd.bus << ":" << dbd.device << std::endl;
},
[] (auto bdf) {
std::cout << bdf.bus << ":" << bdf.device << "." << bdf.function << std::endl;
}
);
}
std::cout << "Done!" << std::endl;
} catch(const std::exception& e) {
std::cout << e.what() << std::endl;
}
return 0;
}
(you can check it on Coliru).
Feel free to use optionals or a separate format field if you don't like special values.
I'd make both the domain and the function optional (I don't really care how, as long as it's effective), and just enforce the only-one-missing condition as a class invariant. That is, only the functions that can change any of the fields need to perform the check and signal possible errors back to the user. No need to bloat your code with variants, or with dynamically interpreted unsigned int arrays. KISS.

The more productive passage by the elements std::vector<struct>

I have struct, which consist of a 30 elements different type.
struct my_struct {
int name1;
int name2;
int name3;
long long name4;
string name5;
//...
double name32;
double name33;
};
Also, i have vector, where save data(very much data) which have type of a struct std::vector<my_struct> my_vector;
Besides, i have so function:
char* name_function(char*){
//...some code
return char*;//processed value with type char*
}
What me need: take every element from vector, then take every value(name1,name2,..., name33) from struct, and then call function to every value, and after it write processing every value in new struct.
How i can see solution:
for(int x=0;x<my_vector.size();x++){
my_struct struct1 = my_vector[x];
char name1 = (char)struct1.name1;
struct1.name1 = int(my_function(&name1));
char name2= (char)struct1.name2;
struct1.name2 = double(my_function(&name2); //invalid type conversion
//...
my_vector[x]=struct1;
}
But, it takes a lot of memory, because i have to create many copies of the elements, maybe there is some more optimal solution for this task?
if you need to encrypt your data, you would need to write your function properly:
void rawencrypt( char *data, size_t size ) // encrypts data in place
{
for( size_t i = 0; i < size; ++i )
data[i] ^= 255; // lame xor encryption for example
}
template<typename T>
void encrypt( T &data ) { rawencrypt( static_cast<char *>( &data ), sizeof( T ) ); }
but for std::string it must be specialized:
template<>
void encrypt( std::string &data ) { rawencrypt( data.data(), data.length() ); }
and you must specialize encrypt() for any non POD data that you intend to encrypt. Encrypting your struct in place would be trivial now:
for(my_struct& s : my_vector){
encrypt( s.name1 );
encrypt( s.name2 );
//...
}
or you can even specialiize encrypt() for my_struct:
template<>
void encrypt( my_struct &data )
{
encrypt( data.name1 );
encrypt( data.name2 );
...
}
and loop would become even simpler:
for(my_struct& s : my_vector)
encrypt( s );
or if do not have C++11:
for(size_t x=0;x<my_vector.size();x++)
encrypt( my_vector[x] );
There is no need to copy the elements. Use references instead, and do proper int/string/int conversion:
char buf[28];
for(int x=0;x<my_vector.size();x++){
my_struct& s = my_vector[x];
itoa(s.name1, buf, 10);
buf = myfunction(buf);
s.name1 = atoi(buf);
//...
s.name5 = myfunction(s.name5.c_str());
//...
}
If you have C++11 or later:
char buf[28];
for(my_struct& s : my_vector){
itoa(s.name1, buf, 10);
//...
}
You can write directly
my_vector[x].name1 = int(my_function((char)my_vector[x].name1));
but these conversions are highly dubious.

Best stl container for sql parameters

I have to build a dynamic sql query. To proper execute it I have to do it in 3 steps:
Prepare statement
Bind Parameters with functions: bindString(string value, int index); bindInt(int value, int index);
Execute it
Because of the fact, that this query is build dynamically I have to store somewhere proper values for given index.
For example:
SELECT * FROM Table WHERE A = ? AND E = '?';
SELECT * FROM Table WHERE A = ? AND B = ? AND E = '?';
During building query I have to store somewhere that:
In the first case:
index 0 is for int A,
index 1 is for string E
In the second case:
index 0 is for int A
index 1 is for int B
index 2 is for string E
My best idea is to create two maps: < int, string >, < int, int > and during creating query set in first place indexes and in second place values and then creating two loops, one for strings, the second one for integers and binding parameters in them and it works fine.
However I wonder if is it possible to do everything in one loop using succeeding indexes and in type safety way.
Thank You.
I would consider creating a class to wrap SQL parameters.
In fact I would create an abstract class like that :
SQLParameterBase
{
std::string toString() = 0;
void print()
{
std::cout << toString();
}
}
And then a template class :
template<class ParamType>
SQLParameter : public SQLParameterBase
{
private:
ParamType value;
public:
std::string toString()
{
// You can use std::ostringstream to convert to string,
// or create another class (derivated from SQLParameterBase) with very specific values
}
}
And you could use it like that :
SQLParameterBase * params[10];
maps[0] = new SQLParameter<int>();
Hope that will help
Actually it is modified AMDG solution. Thanks to him!
class SQLParam {
public:
virtual ~SqlParam(){}
void bind(DatabaseHandler &db, int index) = 0;
};
class SQLParamInt {
private:
int value;
public:
SqlParamInt(int p_value) : value(p_value) {
}
~SqlParamInt() {}
int bind(DatabaseHandler &db, int index) {
return db.bindInt(value, index);
}
};
class SQLParamString {
private:
string value;
public:
SqlParamString(std::string p_value) : value(p_value) {
}
~SqlParamString() {}
int bind(DatabaseHandler &db, int index) {
return db.bindString(value, index);
}
};
typedef std::vector<std::unique_ptr<SqlParam>> SqlParamsContainer;
typedef std::unique_ptr<SqlParamInt> SqlParamIntPtr;
typedef std::unique_ptr<SqlParamString> SqlParamStringPtr;
In my function, building query:
int buildQuery(RequestHandler &request) {
SqlParamsContainer params;
stringstream query << "SELECT * FROM Table WHERE A = ?";
params.push_back(SqlParamIntPtr(new SqlParamInt(request.A())));
if(request.has_B()) {
params.push_back(SqlParamIntPtr(new SqlParamInt(request.B())));
query << " AND B = ?";
}
if(request.has_C()) {
params.push_back(SqlParamStringPtr(new SqlParamString(request.C())));
query << " AND C = ?";
}
query << ";";
db.prepare(query.str());
for(int i = 0; i < v_container.size(); i++)
v_container.at(i)->bind(db,i);
}
There is Boost::Any while it is more general than what you ask for and does not prevent the user from storing unsupported types you do not need to worry about creating the according subclasses.
If you want to return results as well from your DB Boost::Any might be the answer as well.
I suggest limiting the types in your bind function rather than in the storage. If you work with a variadic bind function this is necessary anyways.

Associating text strings with functions

Here's my solution, which requires a test run to compute the const values before it is functional. That's awkward, do you have a more graceful solution? I was thinking I'd avoid calling 100 string comparisons doing it this way.
//find these values before hand
char a[] = "SHET"; //1413826643
char b[] = "ANIM"; //1296649793
char c[] = "RGST"; //1414743890
int* get_word_value[] = { (int*)a, (int*)b, (int*)c };
//enum in header file makes it externally linked and compatible with switch statement
enum str_command{ SHET = 1413826643, ANIM = 1296649793, RGST = 1414743890 } ;
//cpp:
char* file = readTXT( "text.txt" );
int* command = (int*)file;
switch( command ) {
case SHET: SHETfunc( file ); break;
case ANIM: ANIMfunc( file ); break;
case RGST: RGSTfunc( file ); break;
}
Why not
std::map<std::string, std::function<here goes what the return types etc are>> dispatch;
Then
dispatch["a string"](args);
You can initialise the list with map_list_of from boost
Another popular method is to use a look up table:
typedef void (*Function_Pointer)(/* input parameters*/);
struct Name_Function_Entry
{
std::string name;
Function_Pointer function;
};
void Shet(void);
void Anim(void);
void Rgst(void);
const Name_Function_Entry function_table[] =
{
{"SHET", Shet},
{"ANIM", Anim},
{"RGST", Rgst},
};
const unsigned int number_of_entries =
sizeof(function_table) / sizeof(function_table[0]);
// ...
for (unsigned int i = 0; i < number_of_entries; ++i)
{
if (name == function_table[i].name)
{
(function_table[i].function)();
break;
}
}

C++ Structure with unknown data types

My program reads user's input and create easy "table". User on the start specifies data types of columns and the number of rows.
User's input:
create table
add attribute string Name
add attribute int Age
rows 3
I need prepare a structure from user's input now. I have something like this:
CTable
{
unsigned attributesCnt;
string * attributesNames;
void ** attributes;
};
So, from the user's input, the progam does these steps:
CTable myTable;
myTable.attributesCnt = 2; // string "Name", int "Age"
myTable.attributesNames = new string[2];
myTable.attributesNames[0] = "Name";
myTable.attributesNames[1] = "Age";
attributes = new void[2]; // 2 attributes
attributes[0] = (void*) new string[3]; // there will be 3 rows
attributes[1] = (void*) new int[3];
I need remember that "attributes[0]" is string and "attributes[1]" is int too.
Is this "right" way?
I would like use only standard libraries.
What you are looking for is a tagged union also called a variant. It allows you to store multiple data types at the same location just like a regular union but includes an additional but separate data member that indicates it's type. The C++ Standard Library does not include variants but they are easy enough to implement.
Once you have a variant you can apply it to your example like below.
myTable.attributesNames[0] = "Name";
myTable.attributesNames[1] = "Age";
// I recommend using std::vector here instead of using new/delete yourself
attributes = new Variant*[2]; // 2 attributes
attributes[0] = new Variant("player name");
attributes[1] = new Variant(player_age);
The following example shows how the variant might be implemented.
struct Variant
{
enum Type
{
INT,
STRINGPTR
};
Type type_;
union
{
int int_;
const char* stringptr_;
} data_;
explicit Variant(int data) : type_(INT)
{
data_.int_ = data;
}
explicit Variant(const char *data) : type_(STRINGPTR)
{
data_.stringptr_ = data;
}
Type getType() const { return type_; }
int getIntValue() const
{
if(type_ != INT)
throw std::runtime_error("Variant is not an int");
return data_.int_;
}
const char *getStringPtr() const
{
if(type_ != STRINGPTR)
throw std::runtime_error("Variane is not a string");
return data_.stringptr_;
}
};
int main()
{
Variant intval(1);
Variant stringval("hello");
std::cout << intval.getIntValue() << std::endl;
std::cout << stringval.getStringPtr() << std::endl;
}