void pointer usage

I realize that void pointers should be used as a last resort but the need arose and I can't figure out what's wrong. I have an object

class foo {

public :
foo(std::string name,
void* mystruct) : m_name(name),
m_struct(mystruct) {};

void* get_struct();

private :

std::string m_name;
void* m_struct;

};

I'm using a void pointer because I want to push "foo" into a std::map and mystruct could be multiple types. So ...

std::map<std::string, foo> mymap;

foo myA(bob, myAstruct);
foo myB(alice, myBstruct);

mymap["bob"] = myA;
mymap["alice"] = myB;

I have a function

void* get_struct(std::string& myfoo) {
return (mymap[myfoo]).get_struct();
}

I would cast it back to myBstruct* like this

myBstruct* returned = static_cast<myBstruct*>(get_struct("alice"));

What I get back out is myBstruct without the original values although my debugger says it's pointing at the same address

Any ideas?

Thanks
How are myAstruct and myBstruct created? If they are local variables that go out of scope, then the original values could very easily be overwritten. These structures must have a lifetime of at least as long as the elements in the map. From the snippet you have here, you might want to consider who owns the m_struct pointers. Smart pointers would certainly be your friend here.
But wouldn't the smart pointer have the same issue? It may be the way I architected it. The structures are generated from a parser...the map is retrieved after the parsing by using

void generate() { /* structures created here and pushed into the map*/}

void get(std::shared_ptr<dataobj>& data) {
std::shared_ptr<dataobj> tmp = std::make_shared<dataobj>(mymap, mylist);
data.swap(tmp);
}

By this point though, the void pointers in mymap are missing the correct values because they were created in a different method of the class.

I see what you're saying...the structures were created in a different function in this class, so they are going out of scope. I guess I'll need to create the dataobj outside of the parser and pass it in
mystruct could be multiple types

how about templating mystruct then?
I'm using a void pointer because I want to push "foo" into a std::map and mystruct could be multiple types.


Polymorphism? With smart pointers.

A pointer to derived class is a valid pointer to base class. Declare container taking pointer to base, populate with pointer to derived. Virtual functions will do the right thing.
Consider using std::any / std::experimental::any / boost::any as the mapped_type.
http://en.cppreference.com/w/cpp/utility/any
http://en.cppreference.com/w/cpp/experimental/any
http://www.boost.org/doc/libs/1_63_0/doc/html/any.html

For instance, with Visual Studio 2017, -std:c++latest (that is the dfault)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
#include <iostream>
#include <map>
#include <any>
#include <string>
#include <vector>
#include <typeinfo>

using name_value_map = std::map< std::string, std::any > ;

template < typename T > T* get_ptr( name_value_map& map, const std::string& name )
{
    const auto iter = map.find(name) ;
    if( iter != map.end() )
        return std::any_cast<T>( std::addressof(iter->second) ) ;
    else return nullptr ;
}

template < typename T > const T* get_ptr( const name_value_map& map, const std::string& name )
{
    const auto iter = map.find(name) ;
    if( iter != map.end() )
        return std::any_cast<T>( std::addressof(iter->second) ) ;
    else return nullptr ;
}

template < typename T > T& get( name_value_map& map, const std::string& name )
{
    auto ptr = get_ptr<T>( map, name ) ;
    if( ptr == nullptr ) throw std::domain_error( "not found / wrong type" ) ;
    return *ptr ;
}

template < typename T > const T& get( const name_value_map& map, const std::string& name )
{
    auto ptr = get_ptr<T>( map, name ) ;
    if( ptr == nullptr ) throw std::domain_error( "not found / wrong type" ) ;
    return *ptr ;
}

int main()
{
    name_value_map map ;

    map["name_one"] = std::string( "hello" ) ;
    std::cout << get<std::string>( map, "name_one" ) << '\n' ;

    map["name_two"] = 5 ;
    get<int>( map, "name_two" ) *= 100 ;

    map["sequence"] = std::vector<int> { 0, 1, 2, 3, 4, 5 } ;
    get< std::vector<int> >( map, "sequence" ).push_back(6) ;
    for( int v : get< std::vector<int> >( map, "sequence" ) ) std::cout << v << ' ' ;
    std::cout << '\n' ;

    std::cout << "------------------------------------ - \n" ;
    for( const auto& pair : map )
        std::cout << "key: " << pair.first << " => " << pair.second.type().name() << '\n' ;
    std::cout << "------------------------------------ - \n" ;

    map["name_one"] = 1234567890123456LL ;
    std::cout << get<long long>( map, "name_one" ) << '\n' ;

    try { std::cout << get<std::string>( map, "name_one" ) << '\n' ; }
    catch( const std::exception& ) { std::cout << "*** error: get<std::string> failed\n" ; }

    double* ptr = get_ptr<double>( map, "bad name" ) ;
    if(ptr) std::cout << *ptr << '\n' ;
    else std::cout << "*** error: get_ptr<double> failed\n" ;
}

hello
0 1 2 3 4 5 6
------------------------------------ -
key: name_one => class std::basic_string<char,struct std::char_traits<char>,clas
s std::allocator<char> >
key: name_two => int
key: sequence => class std::vector<int,class std::allocator<int> >
------------------------------------ -
1234567890123456
*** error: get<std::string> failed
*** error: get_ptr<double> failed


If the set of mapped types is bounded at compile time,
std::variant / std::experimental::variant / boost::variant would be more appropriate.
@JLBorges

Wow, my eyes hurt from reading the templates.
Last edited on
Speaking of templates ... would a template class work here? Might be overkill just to change the pointer type.

I'm with the above... I think the vp code itself is correct, but your objects were overwritten or destroyed or something to cause the data damage.

void* means it can be of any type you choose. You can read about it from a lot of tutorials among the web. Better know c++ well when you come to code as it can get really complicated and cause errors that can waste your time. As it comes to errors, there are tools that helps detecting them. I use checkmarx sometimes and it works fine. But you can always do that by yourself.
Anyway, good luck!
Ben.
Topic archived. No new replies allowed.