I've done it through manually enumerating the types that I know of, but I'd like to make it more robust than that.
That's what I'd LIKE to do. Is there a way?Code:struct foo {
...
int type;
...
};
struct foo bar;
bar.type = int;
Andy
Printable View
I've done it through manually enumerating the types that I know of, but I'd like to make it more robust than that.
That's what I'd LIKE to do. Is there a way?Code:struct foo {
...
int type;
...
};
struct foo bar;
bar.type = int;
Andy
Just curious why you would want to do something like that?
Well, I've got this really HUGE database to configure on a DSP. The idea struck me that I may want to keep the test app that I make for use in the field (so someone doesn't have to write a program to do it, just make a config file). Well, there are ~100 configurable items in about a dozen structures. So, my idea was to make a VERY generic processor that would, after the "filter" is defined, take the data from the command line / configuration file / stdin and process it into the various structure elements. The way that I've come up with is to enumerate my data typesthen in the processor, I have a case statementCode:enum {
TYPE_FIRST = 0,
TYPE_CHAR = TYPE_FIRST,
TYPE_INT,
TYPE...
}
Now, if only I could pass the type (like within a macro), I could do something more along these linesCode:switch (parameter.type){
case TYPE_CHAR:
charptr = parameter.ref;
*charptr = (char) atoi(userptr);
break;
...
Which would be pretty slick, but I knew that I couldn't do this in C and I only have built a C/C++ cross compiler for the system.Code:parameter.type *ptr;
ptr = parameter.ref;
*ptr = (parameter.type) atoi(userptr);
Was just hoping that someone would know a sneaky shortcut for me.
Hmm I think the closest thing in C to what you want is a union but that doesn't seem to ease things for you. However, you would still have to add all the possible types as members to that union.
Probably foreach may be what you want?
That still doesn't get around the having to have the enumeration, however. So, I'm still going about it the best way possible (I think).
It is VERY LONG coding, though. It turned out to be 11 arrays of structures, 6 or so elements in each struct and about 8 elements in each array. Though, some of it could not be avoided (like the usage string for each -- that just has to be typed out). It took a long time to do all that -- like 11 hours. The rest of it was pretty easy: Two different loops to process the input stream (one for the command line interface and the other for the the input stream (file, stdin -- what's the difference?).
Still, though, I think it is FORTRAN or maybe lisp that I've used before that enable me to pass the data type as a parameter (maybe prolog???) so that one could truly create a generic assign from a pointer.
That's been over....... I'm feeling old tonight.
hmm... if you have a C++ cross compiler at hand, couldn't you use a class template and function templates?
You seem to be using the wrong tool. If it's generic you need, then C++ is a better choice. Dynamically choosing a type in C++ might not be that far-fetched.
Okay, I'll "byte". . .
Given a packed C structure (I HAVE TO USE -- This is the interface to the driver), how do I make a C++ class that will wrapper around the structure so that I can complete the structure from the command line arguments.
An example of a structure would beThe user space parameters would be named what you'd expect (for this case) "CHANNEL", "TYPE", "CHANGES", and "RATE".Code:#define __PACK1 __attribute__((aligned (1),packed))
typedef struct _ADDEF {
uint16_t channel;
uint16_t sample_type;
uint8_t report_changes;
uint16_t sample_time;
} __PACK1 ADDEF;
My approach is to have a structure that defines the user space content, and maps it directly to the element of the structure via a void *. Then, I have another var that gives me what type it really is so that I can deference the pointer correctly.
I'm all ears (eyes, actaully) laserlight, Elysia, and anyone else who can show me.
Basically, in C++ it would be something like:
Now, the question is how you would get the type? C++ allows for very easy passing of the type, assuming you can get it statically (not dynamically).Code:template<typename T> void AssignParameter(const char* userptr)
{
T * ptr = parameter.ref;
*ptr = (T)strtol(userptr);
}
But not knowing exactly how it's going to be done, or any other information, this is pretty much all I can say for now.
The way I've done it so far is with the following structureSo, from the above, the structure would be populated like this for an ADDEF structure:Code:struct parameters {
char *name; // Single string parameter name
char needed; // Is this a required parameter
char arg; // Is there an argument associated
char argneeded; // Is the argument required
char *usage; // Free format usage text
int *type; // Array of the ENUMS above
void *ref; // Pointer(s) to the location in the structure.
struct special *special;// Pointer to ARRAY of special combinations.
};
Code:ADDEF addef;
struct parameters addef_params[] = {
{
.name = "CHANNEL",
.needed = 1,
.arg = 1,
.argneeded = 1,
.usage = "%s=<Channel Number>",
.type = {TYPE_SHORT, -1},
.ref = {&addef.channel, NULL},
},
{
.name = "TYPE",
.needed = 1,
.arg = 1,
.argneeded = 1,
.usage = "%s=<RAW|AVERAGE>",
.type = {TYPE_SHORT, -1},
.ref = {&addef.sample_type, NULL},
.special = {{"RAW", 0x0001, 0}, {"AVERAGE", 0x0020, 0}},
},
. . .
};
Some time back I posted the C-ish source for a variant which sounds like what you want/need. It carried its own RTTI (run time type identification) member (an enumeration if I recall) and a void *ptr and a size. Then it could be a char, string, int, double, MyStruct, whatever. Variants without really good compiler support can be tough but sometimes they are the answer, particularly if you want something like the C-equivalent of a class-factory...
If you can do it in C++, then it's certainly doable in C. I would do it with callbacks and strings, you register a callback and the arguments it takes. Then make everything "stringisable", and call the callback with the usual "int argc, char * argv[]" flavour. Parsing the type is left to whoever supplies the callback.
Sure, never meant to imply otherwise; just that using overloaded functions on a struct allowed for code like:
Where it gets fun is making a variant that can also be a container like an array and make it know its own type (CVariant in this case). That way you could have containers of containers of .. etc. All with a single variable type. Made DB and socket IO pretty easy even if the class (this was in C++) was somewhat complex. Once you can stringify the data it becomes easy to write over a socket, etc. and reconstruct on the distant end.Code:CVariant A, B, C;
A = 42;
B = "Hello";
C = MyClass;
// etc
Well, assuming you've already got the parsing bit worked out, I'll just address the type-mapping aspect.
Basically, what you need are templates. But templates only provide compile-time functionality - you need to generate them at run-time, and for that, virtual functions are needed. Unfortunately, to be practical, dynamic allocation (managed by a 'smart-pointer', preferably) with deep-copying functionality is required. I'll provide a working example in just a moment, but first, let's start by defining a template function that can be used to convert one type to another, using C++ iostreams:
Example usage:Code:template < typename Source, typename Destination >
bool convert( Source const& lhs, Destination& rhs )
{
std::stringstream
io;
if
(
!( io << lhs )
||
!( io >> rhs )
||
io.fail( )
||
io.get( ) != EOF
)
return false;
return true;
}
Okay, now for the more complicated bit. We have a class parameterized by the type that it converts *from* (the "source", typically an std::string), that stores a reference to some arbitrary type (the "destination").Code:int main( void )
{
using namespace
std;
int
value;
if( !convert( "1024", value ) )
cerr << "Error: invalid format" << endl;
else
cout << "The value is: " << value << endl;
}
And here is a crude example of how it is used:Code:template < typename Source >
class stored_converter
{
protected:
struct dynamic_converter_base
{
virtual dynamic_converter_base* clone( void )
{
return new dynamic_converter_base( );
}
virtual bool process( Source const& )
{
return false;
}
};
template < typename Destination >
struct dynamic_converter_derived : dynamic_converter_base
{
dynamic_converter_derived( Destination& result )
: result( result )
{ }
virtual dynamic_converter_base* clone( void )
{
return new dynamic_converter_derived( result );
}
virtual bool process( Source const& data )
{
return convert( data, result );
}
Destination&
result;
};
std::auto_ptr< dynamic_converter_base >
cvt;
public:
stored_converter( void )
: cvt( new dynamic_converter_base( ) )
{ }
stored_converter( stored_converter const& rhs )
: cvt( rhs.cvt->clone( ) )
{ }
template < typename Destination >
stored_converter( Destination& result )
: cvt( new dynamic_converter_derived< Destination >( result ) )
{ }
stored_converter& operator = ( stored_converter const& rhs )
{
cvt = std::auto_ptr< dynamic_converter_base >( rhs.cvt->clone( ) );
return *this;
}
bool operator ( )( Source const& data )
{
return cvt->process( data );
}
};
Make sense?Code:struct settings
{
double
d;
int
i;
string
s;
char
c;
template < typename Ostream >
friend Ostream& operator << ( Ostream& os, settings const& rhs )
{
return os << "{ " << "d: " << rhs.d << ", i: " << rhs.i << ", s: \"" << rhs.s << "\", c: '" << rhs.c << "' }";
}
};
int main( int argc, char** argv )
{
using namespace
std;
typedef stored_converter< string >
converter_t;
typedef map< string, converter_t >
map_t;
settings
info = { 0, 0, "", ' ' };
map_t
table;
table[ "i" ] = converter_t( info.i );
table[ "s" ] = converter_t( info.s );
table[ "d" ] = converter_t( info.d );
table[ "c" ] = converter_t( info.c );
string
name,
value;
cout << "Pass name/value pairs to command line (no separator)" << endl;
cout << "Current settings: " << info << endl;
if( argc == 1 )
return 0;
if( argc % 2 == 0 )
{
cerr << "Error: invalid input" << endl;
return 1;
}
for( ++argv; *argv; argv += 2 )
{
name = *argv;
value = *( argv + 1 );
map_t::iterator
found = table.find( name );
if( found == table.end( ) )
{
cerr << "Error: nonexistant setting '" << name << "'" << endl;
return 2;
}
if( !( found->second( value ) ) )
{
cerr << "Error: invalid value '" << value << "'" << endl;
return 3;
}
}
cout << "Current settings: " << info << endl;
return 0;
}