Thread: Multiple types in lists, vectors or arrays.

  1. #1
    Registered User
    Join Date
    Aug 2006
    Posts
    28

    Multiple types in lists, vectors or arrays.

    I learnt to program in python and php, so i'm used to having dynamic and associative lists (php's array, and pythons dictionary). As c++ requires you define the type of each variable, and define the type used when initialising a vector or list, I have been unable to find something which could emulate what I used in php & python.

    Is there something within the standard library which allows this kind of data structure? Or is this a drawback I have to work around when coding in c++?

  2. #2
    System Novice siavoshkc's Avatar
    Join Date
    Jan 2006
    Location
    Tehran
    Posts
    1,246
    You need a list that can keep any kind of element right?
    Like:
    char-char-string-int-int-bool
    Learn C++ (C++ Books, C Books, FAQ, Forum Search)
    Code painter latest version on sourceforge DOWNLOAD NOW!
    Download FSB Data Integrity Tester.
    Siavosh K C

  3. #3
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,412
    I think you could consider boost::any. That said, C++ is indeed different from PHP and Python, so are you sure you really need such containers in the first place?
    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

  4. #4
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    You shouldn't change C++, you should change the way you think. While variant types are possible (Boost.Any, Boost.Variant), they're very rarely used - I've never used Variant, although I'm always on the lookout for a chance. But every time, some other option that does not risk type safety comes up as the better choice.

    Type safety is not a drawback. It's an advantage.
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

  5. #5
    Registered User
    Join Date
    Aug 2006
    Posts
    28
    I'll have a look at boost::any although I was hoping it was possible within the standard library.

    The reason I need this is that i'm building an xmlrpc client to communicate with a server. I want a dynamic function that can handle all types which may be sent over the xmlrpc protocol (int, bool, string & array). It must also be able to accept any number of arguments (as different calls require different numbers of arguments). If I was using php or python I would simply pass an array or list of all the arguments to the send function and iterate over them.

    There is probably something in c++ which would allow me to do this, preferably within the standard library. However i'm new to c++ and unaware of most of its features.

  6. #6
    Registered User
    Join Date
    Jan 2005
    Posts
    7,366
    If I rememebr correctly, boodt::variant is more appropriate for when you know ahead of time which datatypes are allowed. I believe boost::any is better for when it truly coul dbe any datatype.

    I found this example of boost variant in a standard C++ container, it might help give you an idea of how to do it:
    Code:
    #pragma warning(disable: 4512)
    
    #include <iostream>
    #include <deque>
    #include <string>
    #include <algorithm>
    #include <boost/variant.hpp>
    
    
    typedef std::deque<boost::variant<double, std::string> > data_row;
    typedef std::deque<data_row> data_grid;
    void OutputGrid(const data_grid& the_grid);
    
    class column_sorter
    {
    public:
        column_sorter(data_row::size_type column) : column_(column) { }
        bool operator()(const data_row& left, const data_row& right)
        {
            return left[column_] < right[column_];
        }
    private:
        data_row::size_type column_;
    };
    
    int main()
    {
        // start with 3 empty rows
        data_grid the_grid(3);
    
        // Fill those rows with two columns, int and string.
        the_grid[0].push_back(37);
        the_grid[0].push_back(std::string("thirty-seven"));
        the_grid[1].push_back(-1);
        the_grid[1].push_back(std::string("negative one"));
        the_grid[2].push_back(88);
        the_grid[2].push_back(std::string("eighty-eight"));
    
        OutputGrid(the_grid);
    
        // Add two rows with equal string values.
        data_row new_row;
        new_row.push_back(74);
        new_row.push_back(std::string("seventy-four"));
        the_grid.push_back(new_row);
        new_row[0] = 73;
        the_grid.push_back(new_row);
    
        // Insert a row.
        new_row[0] = -2;
        new_row[1] = std::string("negative two");
        the_grid.insert(the_grid.begin() + 2, new_row);
    
        OutputGrid(the_grid);
    
        // Sort by the second column.
        std::stable_sort(the_grid.begin(), the_grid.end(), column_sorter(1));
    
        OutputGrid(the_grid);
    
        // Sort by the first column.
        std::stable_sort(the_grid.begin(), the_grid.end(), column_sorter(0));
    
        OutputGrid(the_grid);
    
        // Sort by the second column again (notice the 73 and 74 remained the same).
        std::stable_sort(the_grid.begin(), the_grid.end(), column_sorter(1));
    
        OutputGrid(the_grid);
    
        std::cin.get();
    }
    
    void OutputGrid(const data_grid& the_grid)
    {
        for (data_grid::const_iterator cur_row = the_grid.begin(),
            end_row = the_grid.end();
            cur_row != end_row; ++cur_row)
        {
            for (data_row::const_iterator cur_val = cur_row->begin(),
                end_val = cur_row->end();
                cur_val != end_val; ++cur_val)
            {
                if (cur_val != cur_row->begin())
                    std::cout << ", ";
                std::cout << *cur_val;
            }
            std::cout << std::endl;
        }
        std::cout << "\n----------------\n" << std::endl;
    }

  7. #7
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    Hmm ... still not a good idea, I think. XML-RPC follows a pre-defined schema. In other words, it should be possible to know, at the time of writing the code, what types are expected. You would typically write a tool that generates a wrapper class for the web service. This class then presents a fully type-safe interface to the outside.
    The core library, on the other hand, has no need for variants either: it has to convert the values it gets passed in to XML at some point, so why not do it the moment you get the value? This way, you just have a setParameter template (or overloaded) function which takes any datatype, converts it to its wire representation, and stores the resulting string in a homogeneous string container. For the response, you again store the XML representation, until the library user queries the value. Then you require him to specify the suspected type, and try to convert the representation you have into that type. On failing, you could throw an exception, or return an empty Boost.Optional.

    The point is this: at some point, the type decision has to be made, and using variants or any only delays it, transfers it from the interface of the library to the user code, where it does not belong.
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

  8. #8
    Registered User
    Join Date
    Aug 2006
    Posts
    28
    Quote Originally Posted by CornedBee
    Hmm ... still not a good idea, I think. XML-RPC follows a pre-defined schema. In other words, it should be possible to know, at the time of writing the code, what types are expected. You would typically write a tool that generates a wrapper class for the web service. This class then presents a fully type-safe interface to the outside.
    The core library, on the other hand, has no need for variants either: it has to convert the values it gets passed in to XML at some point, so why not do it the moment you get the value? This way, you just have a setParameter template (or overloaded) function which takes any datatype, converts it to its wire representation, and stores the resulting string in a homogeneous string container. For the response, you again store the XML representation, until the library user queries the value. Then you require him to specify the suspected type, and try to convert the representation you have into that type. On failing, you could throw an exception, or return an empty Boost.Optional.

    The point is this: at some point, the type decision has to be made, and using variants or any only delays it, transfers it from the interface of the library to the user code, where it does not belong.
    I've just read up on overloaded template functions and I think i'll do it this way. The only problem is I need a way to tell what type i'm handling when the the argument is processed, so it can be passed to the right function for serialization into an xml-useable string.

    Just to clarify, would the following code give me what i'm after (the server could take up to 5 params of different types per call)..

    Code:
    template <typename T1>
    string sendCall(T1 param1) {
    //code which checks param types and processes to xml string..
    }
    
    template <typename T1, typename T2>
    string sendCall(T1 param1, T2 param2) {
    //code which checks param types and processes to xml string..
    }
    
    template <typename T1, typename T2, typename T3>
    string sendCall(T1 param1, T2 param2, T3 param3) {
    //code which checks param types and processes to xml string..
    }
    
    template <typename T1, typename T2, typename T3, typename T4>
    string sendCall(T1 param1, T2 param2, T3 param3, T4 param4) {
    //code which checks param types and processes to xml string..
    }
    
    template <typename T1, typename T2, typename T3, typename T4, typename T5>
    string sendCall(T1 param1, T2 param2, T3 param3, T4 param4, T5 param5) {
    //code which checks param types and processes to xml string..
    }

  9. #9
    Frequently Quite Prolix dwks's Avatar
    Join Date
    Apr 2005
    Location
    Canada
    Posts
    8,057
    Yes, it would work, but . . . why don't you use default template arguments?
    Code:
    template <typename T1, typename T2 = T1, typename T3 = T2, typename T4 = T3, typename T5 = T4>
    string sendCall(T1 param1, T2 param2, T3 param3, T4 param4, T5 param5) {
    //code which checks param types and processes to xml string..
    }
    or
    Code:
    template <typename T1, typename T2 = int, typename T3 = int, typename T4 = int, typename T5 = int>
    string sendCall(T1 param1, T2 param2, T3 param3, T4 param4, T5 param5) {
    //code which checks param types and processes to xml string..
    }
    dwk

    Seek and ye shall find. quaere et invenies.

    "Simplicity does not precede complexity, but follows it." -- Alan Perlis
    "Testing can only prove the presence of bugs, not their absence." -- Edsger Dijkstra
    "The only real mistake is the one from which we learn nothing." -- John Powell


    Other boards: DaniWeb, TPS
    Unofficial Wiki FAQ: cpwiki.sf.net

    My website: http://dwks.theprogrammingsite.com/
    Projects: codeform, xuni, atlantis, nort, etc.

  10. #10
    Registered User
    Join Date
    Aug 2006
    Posts
    28
    Quote Originally Posted by dwks
    Yes, it would work, but . . . why don't you use default template arguments?
    Code:
    template <typename T1, typename T2 = T1, typename T3 = T2, typename T4 = T3, typename T5 = T4>
    string sendCall(T1 param1, T2 param2, T3 param3, T4 param4, T5 param5) {
    //code which checks param types and processes to xml string..
    }
    or
    Code:
    template <typename T1, typename T2 = int, typename T3 = int, typename T4 = int, typename T5 = int>
    string sendCall(T1 param1, T2 param2, T3 param3, T4 param4, T5 param5) {
    //code which checks param types and processes to xml string..
    }
    Because there are many possible combinations of types used and the order they will be in (the order is important). So i'm not sure what your code does but if it does the same thing then I may use it. Also, is there a way of just writing one function with 5 param's, 4 of which are non-essential and can be NULL when the function is called? Maintaining 5 seperate functions which essentially do the same thing could get tiresome (and buggy).

  11. #11
    Frequently Quite Prolix dwks's Avatar
    Join Date
    Apr 2005
    Location
    Canada
    Posts
    8,057
    Yes, that's what default arguments are for. If you leave out that particular parameter to the function, it gets a default value.
    Code:
    int add(int x, int y = 0) {
        return x + y;
    }
    
    add(5,6);
    add(5); /* same as above */
    The same applies for templates, except instead of default values you use default types.
    Code:
    template <typename T = char>
    size_t size(T x) {
        return sizeof(x);
    }
    
    size<int>();
    size(); /* same as size<char>(); */
    dwk

    Seek and ye shall find. quaere et invenies.

    "Simplicity does not precede complexity, but follows it." -- Alan Perlis
    "Testing can only prove the presence of bugs, not their absence." -- Edsger Dijkstra
    "The only real mistake is the one from which we learn nothing." -- John Powell


    Other boards: DaniWeb, TPS
    Unofficial Wiki FAQ: cpwiki.sf.net

    My website: http://dwks.theprogrammingsite.com/
    Projects: codeform, xuni, atlantis, nort, etc.

  12. #12
    Registered User
    Join Date
    Aug 2006
    Posts
    28
    Quote Originally Posted by dwks
    Yes, that's what default arguments are for. If you leave out that particular parameter to the function, it gets a default value.
    Code:
    int add(int x, int y = 0) {
        return x + y;
    }
    
    add(5,6);
    add(5); /* same as above */
    The same applies for templates, except instead of default values you use default types.
    Code:
    template <typename T = char>
    size_t size(T x) {
        return sizeof(x);
    }
    
    size<int>();
    size(); /* same as size<char>(); */
    Ah, that helps a lot. Thanks!

    EDIT: How do I check if the parameter has been left out. Say if I use the int method, what if an int value is passed to that param. If it is an un-used param i'm guessing it defaults to 0. What if I want to pass an int of value 0?

    EDIT2: When I try to compile I get the following...
    Code:
     error: default template arguments may not be used in function templates
    Last edited by megatron09; 08-30-2006 at 04:51 PM.

  13. #13
    Frequently Quite Prolix dwks's Avatar
    Join Date
    Apr 2005
    Location
    Canada
    Posts
    8,057
    EDIT: How do I check if the parameter has been left out. Say if I use the int method, what if an int value is passed to that param. If it is an un-used param i'm guessing it defaults to 0. What if I want to pass an int of value 0?
    No, it wouldn't have a default value of 0 or anything else, just a default type. To put a default value in, add default values to the function.

    The idea is that you don't need to check if a parameter has been left out. (If you do need to, you use overloading.) It's like getline(); the default separator is '\n'. It doesn't need to know whether you actually passed '\n' or if the '\n' comes from the default value.

    EDIT2: When I try to compile I get the following...

    Code:
     error: default template arguments may not be used in function templates
    Hmm, maybe you can only use them in classes. Sorry, I guess that won't work.
    dwk

    Seek and ye shall find. quaere et invenies.

    "Simplicity does not precede complexity, but follows it." -- Alan Perlis
    "Testing can only prove the presence of bugs, not their absence." -- Edsger Dijkstra
    "The only real mistake is the one from which we learn nothing." -- John Powell


    Other boards: DaniWeb, TPS
    Unofficial Wiki FAQ: cpwiki.sf.net

    My website: http://dwks.theprogrammingsite.com/
    Projects: codeform, xuni, atlantis, nort, etc.

  14. #14
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    Indeed that won't work.

    And no, I didn't mean it that way. I meant a core engine that looks like this:
    Code:
    Call Rpc::createCall(string functionName);
    template<typename T> void Call::setArgument(string argName /*or perhaps an index */, T argValue);
    void Call::submit();
    Calling is rather complicated, but allows for ANY number of arguments. (Your engine won't be generic if it doesn't do that.)
    Code:
    Call call = rpc.createCall("greetWorld");
    call.setArgument("myname", "CornedBee");
    call.setArgument("times", 10);
    call.submit();
    The ease-of-use would come from a tool similar to Axis' wsdl2java that takes a WSDL file and generates a C++ wrapper class from it.
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

  15. #15
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    You should definitely create some wrapper classes that can handle these objects. Derive them from a common base and place common operations that both objects do in the base. Then you can get more specific in the derived classes.

    With this method of using a common base you can then declare an STL container of the base type and yet be able to add both objects since they are derived from the base. This also is a type-safe implementation of doing exactly what you need.

    I would not use variant or any b/c although the functionality is there it cannot be completely safe no matter who wrote it. In essence, C++ provides a much better answer to the problem.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. C++ Vectors versus normal arrays
    By _izua_ in forum C++ Programming
    Replies: 10
    Last Post: 08-12-2007, 01:59 AM
  2. Stl lists and user defined types
    By figa in forum C++ Programming
    Replies: 8
    Last Post: 03-28-2005, 12:09 PM
  3. multiple inputs of multiple types
    By frontz in forum C Programming
    Replies: 8
    Last Post: 01-19-2004, 02:57 PM
  4. Can multiple linked lists share the same structure?
    By passy in forum C Programming
    Replies: 10
    Last Post: 08-28-2003, 04:38 PM
  5. Parallel Arrays with Multiple Arrays
    By Billye Scott in forum C++ Programming
    Replies: 0
    Last Post: 03-02-2002, 11:14 PM