Thread: Is it possible to assign a type to a variable?

  1. #1
    {Jaxom,Imriel,Liam}'s Dad Kennedy's Avatar
    Join Date
    Aug 2006
    Location
    Alabama
    Posts
    1,065

    Is it possible to assign a type to a variable?

    I've done it through manually enumerating the types that I know of, but I'd like to make it more robust than that.

    Code:
    struct foo {
        ...
         int type;
        ...
    };
    
    struct foo bar;
    
    bar.type = int;
    That's what I'd LIKE to do. Is there a way?

    Andy

  2. #2
    Registered User
    Join Date
    Oct 2008
    Location
    TX
    Posts
    2,059
    Just curious why you would want to do something like that?

  3. #3
    {Jaxom,Imriel,Liam}'s Dad Kennedy's Avatar
    Join Date
    Aug 2006
    Location
    Alabama
    Posts
    1,065
    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 types
    Code:
    enum {
        TYPE_FIRST = 0,
        TYPE_CHAR = TYPE_FIRST,
        TYPE_INT,
        TYPE...
    }
    then in the processor, I have a case statement
    Code:
    switch (parameter.type){
        case TYPE_CHAR:
            charptr = parameter.ref;
            *charptr = (char) atoi(userptr);
            break;
    ...
    Now, if only I could pass the type (like within a macro), I could do something more along these lines
    Code:
    parameter.type *ptr;
    ptr = parameter.ref;
    *ptr = (parameter.type) atoi(userptr);
    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.

    Was just hoping that someone would know a sneaky shortcut for me.

  4. #4
    Registered User claudiu's Avatar
    Join Date
    Feb 2010
    Location
    London, United Kingdom
    Posts
    2,094
    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.
    1. Get rid of gets(). Never ever ever use it again. Replace it with fgets() and use that instead.
    2. Get rid of void main and replace it with int main(void) and return 0 at the end of the function.
    3. Get rid of conio.h and other antiquated DOS crap headers.
    4. Don't cast the return value of malloc, even if you always always always make sure that stdlib.h is included.

  5. #5
    Registered User
    Join Date
    May 2010
    Location
    Naypyidaw
    Posts
    1,314
    Probably foreach may be what you want?

  6. #6
    {Jaxom,Imriel,Liam}'s Dad Kennedy's Avatar
    Join Date
    Aug 2006
    Location
    Alabama
    Posts
    1,065
    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.

  7. #7
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    hmm... if you have a C++ cross compiler at hand, couldn't you use a class template and function templates?
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  8. #8
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    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.
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  9. #9
    {Jaxom,Imriel,Liam}'s Dad Kennedy's Avatar
    Join Date
    Aug 2006
    Location
    Alabama
    Posts
    1,065
    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 be
    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;
    The user space parameters would be named what you'd expect (for this case) "CHANNEL", "TYPE", "CHANGES", and "RATE".

    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.

  10. #10
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    Basically, in C++ it would be something like:
    Code:
    template<typename T> void AssignParameter(const char* userptr)
    {
        T * ptr = parameter.ref;
        *ptr = (T)strtol(userptr);
    }
    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).
    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.
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  11. #11
    {Jaxom,Imriel,Liam}'s Dad Kennedy's Avatar
    Join Date
    Aug 2006
    Location
    Alabama
    Posts
    1,065
    The way I've done it so far is with the following 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.
    };
    So, from the above, the structure would be populated like this for an ADDEF structure:
    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}},
            },
            . . .
    };

  12. #12
    Registered User jeffcobb's Avatar
    Join Date
    Dec 2009
    Location
    Henderson, NV
    Posts
    875
    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...
    C/C++ Environment: GNU CC/Emacs
    Make system: CMake
    Debuggers: Valgrind/GDB

  13. #13
    Woof, woof! zacs7's Avatar
    Join Date
    Mar 2007
    Location
    Australia
    Posts
    3,459
    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.

  14. #14
    Registered User jeffcobb's Avatar
    Join Date
    Dec 2009
    Location
    Henderson, NV
    Posts
    875
    Quote Originally Posted by zacs7 View Post
    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:
    Code:
    CVariant A, B, C;
    A = 42;
    B = "Hello";
    C = MyClass;
    // etc
    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.
    C/C++ Environment: GNU CC/Emacs
    Make system: CMake
    Debuggers: Valgrind/GDB

  15. #15
    Guest Sebastiani's Avatar
    Join Date
    Aug 2001
    Location
    Waterloo, Texas
    Posts
    5,708
    Quote Originally Posted by Kennedy View Post
    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 be
    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;
    The user space parameters would be named what you'd expect (for this case) "CHANNEL", "TYPE", "CHANGES", and "RATE".

    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.
    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:

    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;
    }
    Example usage:

    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;
    }
    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:
    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 );
        }
    };
    And here is a crude example of how it is used:

    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;    
    }
    Make sense?
    Code:
    #include <cmath>
    #include <complex>
    bool euler_flip(bool value)
    {
        return std::pow
        (
            std::complex<float>(std::exp(1.0)), 
            std::complex<float>(0, 1) 
            * std::complex<float>(std::atan(1.0)
            *(1 << (value + 2)))
        ).real() < 0;
    }

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. sorting number
    By Leslie in forum C Programming
    Replies: 8
    Last Post: 05-20-2009, 04:23 AM
  2. how do you resolve this error?
    By -EquinoX- in forum C Programming
    Replies: 32
    Last Post: 11-05-2008, 04:35 PM
  3. Can you check what is wrong with this code
    By Ron in forum C++ Programming
    Replies: 4
    Last Post: 08-01-2008, 10:59 PM
  4. Replies: 6
    Last Post: 07-29-2008, 04:37 AM
  5. Replies: 28
    Last Post: 07-16-2006, 11:35 PM