Heterogeneous collection: return type overload

Discussion in 'C++' started by jrbcast, Feb 17, 2010.

  1. jrbcast

    jrbcast Guest

    Hi all,

    Imagine I have something like this:

    class Collection
    {
    std::map<char *, void *> elements;
    Collection();
    ~Collection();

    addElement( char * name, void * value )
    {
    }

    /*** I Want to merge next methods into a single one ***/
    int getValue( char * name )
    {
    }

    double getValue( )
    {
    }

    }

    The purpose of this code would be to have a collection of objects of
    different kind. Supposing that inside the class I would be able to
    know which is the type of each element... How could I implement a
    single function to return the right type?

    Thanks in advance
    jrbcast, Feb 17, 2010
    #1
    1. Advertising

  2. jrbcast wrote:
    > Imagine I have something like this:
    >
    > class Collection
    > {
    > std::map<char *, void *> elements;
    > Collection();
    > ~Collection();
    >
    > addElement( char * name, void * value )
    > {
    > }
    >
    > /*** I Want to merge next methods into a single one ***/
    > int getValue( char * name )
    > {
    > }
    >
    > double getValue( )
    > {
    > }
    >
    > }
    >
    > The purpose of this code would be to have a collection of objects of
    > different kind. Supposing that inside the class I would be able to
    > know which is the type of each element... How could I implement a
    > single function to return the right type?


    What's the "right type"? How do you know which type is right? In most
    systems I've seen folks didn't use the return value. They used an
    argument or a template argument.

    Argument:

    bool getValue(char const* name, int& ri)
    {
    // assign to 'ri' if found, return true
    // if not found, don't touch 'ri' return false
    }

    bool getValue(char const* name, double& rd) // same thing

    Template argument:

    template<class T> T getValue(char const* name)
    {
    return SomeHelperClassYouCanSpecialize<T>(this, name);
    }

    Both solutions require the caller to know which type is right.
    Otherwise you need a dynamically typed system, and it ain't C++.

    V
    --
    Please remove capital 'A's when replying by e-mail
    I do not respond to top-posted replies, please don't ask
    Victor Bazarov, Feb 17, 2010
    #2
    1. Advertising

  3. jrbcast

    jrbcast Guest

    On Feb 17, 7:59 pm, "Leigh Johnston" <> wrote:
    > "jrbcast" <> wrote in message
    >
    > news:...
    >
    > > Hi all,

    >
    > > Imagine I have something like this:

    >
    > > class Collection
    > > {
    > > std::map<char *, void *> elements;
    > > Collection();

    >
    > Try "std::map<std::string, boost::any> elements;" instead.
    >
    > /Leigh


    Thanks for your responses. Now it is clear that C++ does not support
    function
    overloading on return types :-(.

    Leigh, I have seen boost::any but I can not add new libraries to my
    application,
    that is a constrain....

    Cheers,
    jrbcast, Feb 18, 2010
    #3
  4. On 18 fév, 07:50, jrbcast <> wrote:
    > On Feb 17, 7:59 pm, "Leigh Johnston" <> wrote:
    >
    >
    >
    > > "jrbcast" <> wrote in message

    >
    > >news:....

    >
    > > > Hi all,

    >
    > > > Imagine I have something like this:

    >
    > > > class Collection
    > > > {
    > > > std::map<char *, void *> elements;
    > > > Collection();

    >
    > > Try "std::map<std::string, boost::any> elements;" instead.

    >
    > > /Leigh

    >
    > Thanks for your responses. Now it is clear that C++ does not support
    > function
    > overloading on return types :-(.
    >
    > Leigh, I have seen boost::any but I can not add new libraries to my
    > application,
    > that is a constrain....


    A lot of Boost libraries are header only and will only affect the
    compilation stage. You don't need to redistribute Boost library; and
    the license is rather business friendly.

    --
    Michael
    Michael Doubez, Feb 18, 2010
    #4
  5. jrbcast

    Jeff Flinn Guest

    Leigh Johnston wrote:
    >
    >
    > "jrbcast" <> wrote in message
    > news:...
    >> On Feb 17, 7:59 pm, "Leigh Johnston" <> wrote:
    >>> "jrbcast" <> wrote in message
    >>>
    >>> news:...
    >>>
    >>>
    >>> > Hi all,
    >>>
    >>> > Imagine I have something like this:
    >>>
    >>> > class Collection
    >>> > {
    >>> > std::map<char *, void *> elements;
    >>> > Collection();
    >>>
    >>> Try "std::map<std::string, boost::any> elements;" instead.
    >>>
    >>> /Leigh

    >>
    >> Thanks for your responses. Now it is clear that C++ does not support
    >> function
    >> overloading on return types :-(.
    >>
    >> Leigh, I have seen boost::any but I can not add new libraries to my
    >> application,
    >> that is a constrain....
    >>
    >> Cheers,

    >
    > Rip boost.any out of Boost and use it standalone then.


    and IIRC, boost.any is a header only library which makes it's use even
    easier. Although I'm not sure if it has dependencies on other boost
    libs. There is a bcp tool that lets you pull out subsets of boost and
    ensures you get all the dependencies.

    Also if you are dealing with a known set of types to be returned
    boost.variant might be a better solution.

    Jeff
    Jeff Flinn, Feb 18, 2010
    #5
  6. jrbcast

    James Kanze Guest

    On Feb 18, 6:50 am, jrbcast <> wrote:
    > On Feb 17, 7:59 pm, "Leigh Johnston" <> wrote:
    > > "jrbcast" <> wrote in message


    > >news:...


    > > > Imagine I have something like this:


    > > > class Collection
    > > > {
    > > > std::map<char *, void *> elements;


    Using a char* as a key in a map is probably not a very good
    idea.

    > > > Collection();


    > > Try "std::map<std::string, boost::any> elements;" instead.


    > Thanks for your responses. Now it is clear that C++ does not
    > support function overloading on return types :-(.


    Not directly, but it's fairly simple to get the same effect by
    means of a proxy:

    class Collection
    {
    public:
    class Proxy
    {
    Collection const* owner;
    std::string key;
    public:
    Proxy(Collection const& owner, std::string const& key)
    : owner(&owner)
    , key(key)
    {
    }
    template< typename T > operator T() const
    {
    return owner->... // whatever it takes to get
    // the correctly typed value
    }
    }
    Proxy get(std::string const& key) const
    {
    return Proxy(*this, key);
    }
    // ...
    };

    --
    James Kanze
    James Kanze, Feb 18, 2010
    #6
  7. jrbcast

    jrbcast Guest

    On Feb 18, 9:04 pm, James Kanze <> wrote:
    > On Feb 18, 6:50 am, jrbcast <> wrote:
    >
    > > On Feb 17, 7:59 pm, "Leigh Johnston" <> wrote:
    > > > "jrbcast" <> wrote in message
    > > >news:....
    > > > > Imagine I have something like this:
    > > > > class Collection
    > > > > {
    > > > > std::map<char *, void *> elements;

    >
    > Using a char* as a key in a map is probably not a very good
    > idea.
    >


    Yes, I realized about that after posting ;-), I am now using
    std::string

    > > > > Collection();
    > > > Try "std::map<std::string, boost::any> elements;" instead.

    > > Thanks for your responses. Now it is clear that C++ does not
    > > support function overloading on return types :-(.

    >
    > Not directly, but it's fairly simple to get the same effect by
    > means of a proxy:
    >
    >     class Collection
    >     {
    >     public:
    >         class Proxy
    >         {
    >             Collection const* owner;
    >             std::string key;
    >         public:
    >             Proxy(Collection const& owner, std::string const& key)
    >                 : owner(&owner)
    >                 , key(key)
    >             {
    >             }
    >             template< typename T > operator T() const
    >             {
    >                 return owner->...  //  whatever it takes to get
    >                                    //  the correctly typed value
    >             }
    >         }
    >         Proxy get(std::string const& key) const
    >         {
    >             return Proxy(*this, key);
    >         }
    >         //  ...
    >     };
    >


    I am afraid I am not able to follow the code (too hard for me and my
    usual needs). Can you point me to some book/web... where to learn such
    special things?

    Cheers

    > --
    > James Kanze
    jrbcast, Feb 18, 2010
    #7
  8. jrbcast

    jrbcast Guest

    On Feb 18, 9:53 pm, "Leigh Johnston" <> wrote:
    > "jrbcast" <> wrote in message
    >
    > news:...
    >
    >
    >
    > > On Feb 18, 9:04 pm, James Kanze <> wrote:
    > >> On Feb 18, 6:50 am, jrbcast <> wrote:

    >
    > >> > On Feb 17, 7:59 pm, "Leigh Johnston" <> wrote:
    > >> > > "jrbcast" <> wrote in message
    > >> > >news:...
    > >> > > > Imagine I have something like this:
    > >> > > > class Collection
    > >> > > > {
    > >> > > > std::map<char *, void *> elements;

    >
    > >> Using a char* as a key in a map is probably not a very good
    > >> idea.

    >
    > > Yes, I realized about that after posting ;-), I am now using
    > > std::string

    >
    > >> > > > Collection();
    > >> > > Try "std::map<std::string, boost::any> elements;" instead.
    > >> > Thanks for your responses. Now it is clear that C++ does not
    > >> > support function overloading on return types :-(.

    >
    > >> Not directly, but it's fairly simple to get the same effect by
    > >> means of a proxy:

    >
    > >>     class Collection
    > >>     {
    > >>     public:
    > >>         class Proxy
    > >>         {
    > >>             Collection const* owner;
    > >>             std::string key;
    > >>         public:
    > >>             Proxy(Collection const& owner, std::string const& key)
    > >>                 : owner(&owner)
    > >>                 , key(key)
    > >>             {
    > >>             }
    > >>             template< typename T > operator T() const
    > >>             {
    > >>                 return owner->...  //  whatever it takes to get
    > >>                                    //  the correctly typed value
    > >>             }
    > >>         }
    > >>         Proxy get(std::string const& key) const
    > >>         {
    > >>             return Proxy(*this, key);
    > >>         }
    > >>         //  ...
    > >>     };

    >
    > > I am afraid I am not able to follow the code (too hard for me and my
    > > usual needs). Can you point me to some book/web... where to learn such
    > > special things?

    >
    > > Cheers

    >
    > Which bit(s) don't you understand?
    >
    > /Leigh


    Thank you very much, I did not realize that "proxy" was the key word
    to google it :-S. Sorry, too late here after work (Spain = 21:54).

    I can not imagine how this proxy hides types management in a
    heterogeneous structure like that I am proposing here std::map<
    std::string, void *> so the caller does not need to explicitly pass
    the type it is expecting, just receive it (the called object will care
    about that)...

    I have written something like this but I would like (if possible) to
    know if there is a more "advanced" or "correct" way to do that:

    // PLEASE NOTE CODE HAS NOT BEEN COMPILED YET AND MAY BE ERRONEOUS

    #define angle_t double

    #define rot(object) *((angle_t*)(object.getValue(std::string("rot"))))
    #define tilt(object) *((angle_t*)
    (object.getValue(std::string("tilt"))))
    #define psi(object) *((angle_t*)(object.getValue(std::string("psi"))))

    class objectValues
    {
    std::map<std::string, void *> values;
    objectValues();
    ~objectValues();

    int addValue( std::string name, int value )
    {
    void * newValue = (void *)(new int(value));
    return insertVoidPtr( name, newValue );
    }

    int addValue( std::string name, double value )
    {
    void * newValue = (void *)(new double(value));
    return insertVoidPtr( name, newValue );
    }

    int insertVoidPtr( std::string name, void * value )
    {
    // Return value for "insert" call
    pair<map<std::string, void *>::iterator,bool> ret;

    ret = values.insert( pair<std::string, void*>(name,value) );

    if (ret.second==false)
    {
    return -1;
    }
    else
    {
    return 0;
    }
    }

    void * getValue( std::string name )
    {
    map <std::string, *void>::const_iterator element;

    element = values.find( name );

    if ( element == values.end( ) )
    {
    return NULL;
    }
    else
    {
    return element->second;
    }
    }

    bool valueExists( std::string name )
    {
    map <std::string, *void>::const_iterator element;

    element = values.find( name );

    if ( element == values.end( ) )
    {
    return false;
    }
    else
    {
    return true;
    }
    }

    void deleteValue( std::string name )
    {
    map <std::string, *void>::const_iterator element;

    element = values.find( name );

    if ( element != values.end( ) )
    {
    values.erase( element );
    }
    }
    }
    jrbcast, Feb 18, 2010
    #8
  9. jrbcast

    DeMarcus Guest

    jrbcast wrote:
    > On Feb 18, 9:53 pm, "Leigh Johnston" <> wrote:
    >> "jrbcast" <> wrote in message
    >>
    >> news:...
    >>
    >>
    >>
    >>> On Feb 18, 9:04 pm, James Kanze <> wrote:
    >>>> On Feb 18, 6:50 am, jrbcast <> wrote:
    >>>>> On Feb 17, 7:59 pm, "Leigh Johnston" <> wrote:
    >>>>>> "jrbcast" <> wrote in message
    >>>>>> news:...
    >>>>>>> Imagine I have something like this:
    >>>>>>> class Collection
    >>>>>>> {
    >>>>>>> std::map<char *, void *> elements;
    >>>> Using a char* as a key in a map is probably not a very good
    >>>> idea.
    >>> Yes, I realized about that after posting ;-), I am now using
    >>> std::string
    >>>>>>> Collection();
    >>>>>> Try "std::map<std::string, boost::any> elements;" instead.
    >>>>> Thanks for your responses. Now it is clear that C++ does not
    >>>>> support function overloading on return types :-(.
    >>>> Not directly, but it's fairly simple to get the same effect by
    >>>> means of a proxy:
    >>>> class Collection
    >>>> {
    >>>> public:
    >>>> class Proxy
    >>>> {
    >>>> Collection const* owner;
    >>>> std::string key;
    >>>> public:
    >>>> Proxy(Collection const& owner, std::string const& key)
    >>>> : owner(&owner)
    >>>> , key(key)
    >>>> {
    >>>> }
    >>>> template< typename T > operator T() const
    >>>> {
    >>>> return owner->... // whatever it takes to get
    >>>> // the correctly typed value
    >>>> }
    >>>> }
    >>>> Proxy get(std::string const& key) const
    >>>> {
    >>>> return Proxy(*this, key);
    >>>> }
    >>>> // ...
    >>>> };
    >>> I am afraid I am not able to follow the code (too hard for me and my
    >>> usual needs). Can you point me to some book/web... where to learn such
    >>> special things?
    >>> Cheers

    >> Which bit(s) don't you understand?
    >>
    >> /Leigh

    >
    > Thank you very much, I did not realize that "proxy" was the key word
    > to google it :-S. Sorry, too late here after work (Spain = 21:54).
    >
    > I can not imagine how this proxy hides types management in a
    > heterogeneous structure like that I am proposing here std::map<
    > std::string, void *> so the caller does not need to explicitly pass
    > the type it is expecting, just receive it (the called object will care
    > about that)...
    >
    > I have written something like this but I would like (if possible) to
    > know if there is a more "advanced" or "correct" way to do that:
    >
    > // PLEASE NOTE CODE HAS NOT BEEN COMPILED YET AND MAY BE ERRONEOUS
    >
    > #define angle_t double
    >
    > #define rot(object) *((angle_t*)(object.getValue(std::string("rot"))))
    > #define tilt(object) *((angle_t*)
    > (object.getValue(std::string("tilt"))))
    > #define psi(object) *((angle_t*)(object.getValue(std::string("psi"))))
    >
    > class objectValues
    > {
    > std::map<std::string, void *> values;
    > objectValues();
    > ~objectValues();
    >
    > int addValue( std::string name, int value )
    > {
    > void * newValue = (void *)(new int(value));
    > return insertVoidPtr( name, newValue );
    > }
    >
    > int addValue( std::string name, double value )
    > {
    > void * newValue = (void *)(new double(value));
    > return insertVoidPtr( name, newValue );
    > }
    >
    > int insertVoidPtr( std::string name, void * value )
    > {
    > // Return value for "insert" call
    > pair<map<std::string, void *>::iterator,bool> ret;
    >
    > ret = values.insert( pair<std::string, void*>(name,value) );
    >
    > if (ret.second==false)
    > {
    > return -1;
    > }
    > else
    > {
    > return 0;
    > }
    > }
    >
    > void * getValue( std::string name )
    > {
    > map <std::string, *void>::const_iterator element;
    >
    > element = values.find( name );
    >
    > if ( element == values.end( ) )
    > {
    > return NULL;
    > }
    > else
    > {
    > return element->second;
    > }
    > }
    >
    > bool valueExists( std::string name )
    > {
    > map <std::string, *void>::const_iterator element;
    >
    > element = values.find( name );
    >
    > if ( element == values.end( ) )
    > {
    > return false;
    > }
    > else
    > {
    > return true;
    > }
    > }
    >
    > void deleteValue( std::string name )
    > {
    > map <std::string, *void>::const_iterator element;
    >
    > element = values.find( name );
    >
    > if ( element != values.end( ) )
    > {
    > values.erase( element );
    > }
    > }
    > }
    >


    You may be in the same situation as I have at a couple of places, where
    I need to store values of any type in a container to be able to process
    them later.

    Following is a simplification of my own solution, but please note that I
    have not found this solution anywhere on the net or in any book, so I
    don't know if this is the right way to go. However, it seems to work
    pretty good so far. The solution is built on the Visitor pattern.
    http://en.wikipedia.org/wiki/Visitor_pattern

    In this solution you first have to identify the receiver of your object
    values. I.e. it may not be the perfect solution if you want to create a
    generic map that can deliver your objects to whatever. In my case I
    wanted to deliver a container of any type to std::cout. Basically I
    wanted to create a container with contents like this
    { "Hello ", myOwnObject, " number ", 7, std::endl } and I couldn't
    calculate it during the call, hence, I first needed to store the values
    in a container until they should be used.

    Ok, back to step one. I identified the receiver as std::cout. Then I
    created an objectValue-to-receiver interface like this.

    class ObjectValueInterface
    {
    public:
    virtual ~ObjectValueInterface() {}
    virtual void applyTo( std::eek:stream& ) = 0;
    // applyTo() would represent accept() in the Visitor pattern.
    };

    // Then create a template for any object value like this.
    // Here you also need to decide whether you want a copy of your object
    // or a (smart) pointer to your object. Since you copy in your own
    // example I do the copy variant here.

    template<typename T>
    class ObjectValue : public ObjectValueInterface
    {
    public:
    ObjectValue( T objVal ) : objVal_(objVal) {}
    virtual void applyTo( std::eek:stream& os )
    {
    os << objVal_;
    // operator<<() would represent visit() in the Visitor pattern.
    }
    private:
    T objVal_;
    };

    // Now you create your object value container.
    #include <map>

    class ObjectValueContainer
    {
    public:
    template<typename T>
    int addValue( const std::string& key, T val )
    {
    if( objVals_.find( key ) != objVals_.end() )
    return -1;

    objVals_[key] = new ObjectValue<T>( val );

    return 0;
    }

    ObjectValueInterface* getValue( const std::string& key )
    {
    if( objVals_.find( key ) == objVals_.end() )
    return NULL;

    return objVals_[key];
    }
    private:
    std::map<std::string, ObjectValueInterface*> objVals_;

    // Note! This is a simplified example. You should consider using
    // boost::shared_ptr instead of a plain pointer here.
    // boost::shared_ptr will be std::shared_ptr with the next
    // standard. Until then, see
    // www.boost.org/doc/libs/1_42_0/libs/smart_ptr/shared_ptr.htm

    };

    // Now use it.
    // Note! In this example we skip error handling.

    int main()
    {
    ObjectValueContainer objVals;

    // Add some values.
    objVals.addValue( "Name", "Daniel" );
    objVals.addValue( "House Number", 3 );
    objVals.addValue( "Double", 47.11 );
    objVals.addValue( "Delimiter", " " );

    // Print the values.
    objVals.getValue( "Name" )->applyTo( std::cout );
    objVals.getValue( "Delimiter" )->applyTo( std::cout );
    objVals.getValue( "House Number" )->applyTo( std::cout );
    objVals.getValue( "Delimiter" )->applyTo( std::cout );
    objVals.getValue( "Double" )->applyTo( std::cout );

    return 0;
    }

    Hope this helps!
    If anyone else have comments on this kind of solution I would be happy
    to hear it so I can find flaws and improve it.

    /Daniel
    DeMarcus, Feb 20, 2010
    #9
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Piotre Ugrumov
    Replies:
    3
    Views:
    368
    Nick Hounsome
    Jan 25, 2004
  2. Fabio Fracassi
    Replies:
    5
    Views:
    326
  3. Shea Martin

    overload on return type

    Shea Martin, Apr 8, 2006, in forum: C++
    Replies:
    5
    Views:
    350
    Shea Martin
    Apr 9, 2006
  4. Øyvind Isaksen
    Replies:
    1
    Views:
    958
    Øyvind Isaksen
    May 18, 2007
  5. Ying-Chieh Liao

    function overload (not operator overload)

    Ying-Chieh Liao, Oct 11, 2004, in forum: Perl Misc
    Replies:
    3
    Views:
    248
    Sherm Pendley
    Oct 11, 2004
Loading...

Share This Page