C to CPP Migration

I am trying to write a C++ solution for a problem which is written C, I have learnt C++ very long ago(may be 15 years), and the way C++ is evolved
now is kind of taking long to understand and not easy as 'C' to develop a solution.
Would request any experts help in solving the problem .

Actually, the problem is quite big , but i broke down into small , so i that i can reuse from learnings here.

I have already started with a small class here to store a list of strings and print them.

But what i need is some good C++ way to store pre-defined enities and then create a mapper dynamically based on these entities.

for example in C i can do it this way

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
enum e_countries {GGG=0,AAA,WWW,FFF,III};
typedef struct Country_t{
    char name[SIZE];
    char flag[SIZE];
    char head[SIZE];
    enum e_countries e;
}Country;
Country listOfCountries[] = { 
    {"GGGG","hjkkd","hhkad",GGG},
    {"AAAA","OOO","Ojkjs",AAA},
    {"WWWW","Ocghaa","Wahja",WWW},
    {"FFFF","Dddaa","Hahada",FFF},
    {"III","Masadaa","Iahjda",III},
                             };
// Create Mapper , for easy and fast accessing of elements based on key 
hashsum = calc_hashsum(listOfCountries[cindex].name);
key = hashsum % (numOfCountries);
MakeCountryMapperHashTable(key,&listOfCountries[cindex],numOfCountries);
// some of methods i can use on mapper later 
    IsConutryUnique(nameofsomecountry);
    hashsum = calc_hashsum(nameofsomecountry);
    key = hashsum % (numOfCountries);
    return strcmp(nameofsomecountry,GetCountry(key));


CPP Solution


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
#include <list>
#include <iostream>
#include <string>
#include <iterator>
#include <algorithm>
class Country
{
private:
    std::string name;
    std::string flag;
    std::string head;
public:
    Country(std::string countryName, std::string flagName,std::string headName) :
    name(countryName), flag(flagName), head(headName)
{
}
Country();
~Country();
void Print();
};
std::list<Country> listofCountries ={ Country("Country1","Flag1","Head1"), Country("Country2", "Flag2","Head2"), Country("Country3", "Flag3","Head3")};
Country::Country(){}
Country ::~Country(){}
void Country::Print()
{
    std::list<Country>::iterator it;
    for (it = listofCountries.begin(); it != listofCountries.end(); it++)
    {
        std::string name = it->name;
        std::string flag = it->flag;
        std::string head = it->head;
        //Print the contents
        std::cout <<" C:" << name <<" F:" << flag <<" H:" << head << std::endl;
    }
}
int main()
{
    Country P1;
    P1.Print();
    return 0;


The class needs to support these
1) Cretae a map with the key as country name
2) Strore Unique Elements:
i want to have country names unique, so i will check the new country with
all the existing countries , and store only if does not existing
Last edited on
look at this: http://www.cplusplus.com/reference/unordered_map/
which is very similar to your original C. Use this instead of the <list> maybe?


Thanks Jonnin for the suggestion, that looks good!

But i want to calcuate the key values based on the country name,for example, if country name is "ABC", i get the key from adding the ascii values of "ABC" and applying some range function so that i can limit my keys.

unordered_map<int,Country> umap;

is Country ok ? or Country * better?

But before that, which data structure is better to store all my country names statically, is structure is good enough as we do in "C", or C++ has other ways.

From that data structure i would like to make a MAP.

Thanks again.
you can do that.
add to the map hash(thiscountry.name), country and you have your hashed key tied to the data. if you put the hashed key IN the data you would do less recomputation of the hashed value, maybe?

if you have country objects already in a container, outside the map, use a pointer back to the originals in the map. If the data will live in the map, just use the type not a pointer. Generally, we don't do a lot of manual dynamic memory in c++ now (new/delete). Pointers are more often just access points to existing things than memory management. Let the containers do that for you behind the scenes.

class and struct are interchangeable, most prefer class. the primary (only?) difference is default public items (struct) vs default private (class) but you can override either. Opinions vary on when to use each, some say no - method types are struct, adding methods means use class. That's probably good enough rule to follow.

map is templated, it can take any type including your struct/class

Last edited on
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
#include <iostream>
#include <string>
#include <map> // or: <unordered_map>
#include <stdexcept>

enum e_countries { GGG=0, AAA, WWW, FFF, III };

struct Country {

    std::string name ;
    std::string flag ;
    std::string head ;
    e_countries e;

    friend std::ostream& operator<< ( std::ostream& stm, const Country& c ) {

        return stm << "Country{ name:" << c.name << " flag:" << c.flag
                   << " head:" << c.head << " e:" << c.e << " }" ;
    }
};

// key: name,  mapped data: Country
// TO DO? make the key (name) comparison case-insensitive?
using country_map = std::map< std::string, Country > ; // or: unordered_map

const Country& look_up( const country_map& map, const std::string& name ) {

    const auto iter = map.find(name) ;
    if( iter != map.end() ) return iter->second ;
    else throw std::domain_error( "lookup failed" ) ;
}

int main() {

    const country_map countries = {

        { "GGGG", { "GGGG", "hjkkd", "hhkad", GGG } },
        { "AAAA", { "AAAA", "OOO", "Ojkjs", AAA } },
        { "WWWW", { "WWWW", "Ocghaa", "Wahja", WWW } },
        { "FFFF", { "FFFF", "Dddaa", "Hahada", FFF } },
        { "IIII", { "IIII", "Masadaa", "Iahjda", III } },
    };

    std::string name ;
    while( std::cout << "name? " && std::cin >> name ) {

        std::cout << '\'' << name << "' : "  ;
        try { std::cout << look_up( countries, name ) << '\n' ; }
        catch( const std::exception& e ) { std::cout << "***error*** " << e.what() << '\n' ; }
    }
}
Thanks Jonnin for the suggestions, they are all helpful
Especially "map hash(thiscountry.name), country and you have your hashed key tied to the data".
I will keep those points in mind.

Thanks JLBorges, i am kind of new to C++, looking at your program, few of my doubts got cleared really, i thought structures cant hold functions, but in c++ it can, even friends.

so as jonnin mentioned we can almost replace class with structure in c++.


I will build up some thing more on this and comeback.

Is there a way here to upvote or give points to good answers as appreciation?

JLBorges, this way of initializing is good

1
2
3
4
5
6
7
8
9
10

const country_map countries = {

        { "GGGG", { "GGGG", "hjkkd", "hhkad", GGG } },
        { "AAAA", { "AAAA", "OOO", "Ojkjs", AAA } },
        { "WWWW", { "WWWW", "Ocghaa", "Wahja", WWW } },
        { "FFFF", { "FFFF", "Dddaa", "Hahada", FFF } },
        { "IIII", { "IIII", "Masadaa", "Iahjda", III } },
    };


But how do we handle duplicates with this?
std::map holds entries with unique keys (no duplicates).
std::multimap can hold more than entry with the same key (allows duplicate keys).

For example:

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
#include <iostream>
#include <string>
#include <map> // or: <unordered_map>
#include <stdexcept>

enum e_countries { GGG=0, AAA, WWW, FFF, III };

struct Country {

    std::string name ;
    std::string flag ;
    std::string head ;
    e_countries e;

    friend std::ostream& operator<< ( std::ostream& stm, const Country& c ) {

        return stm << "Country{ name:" << c.name << " flag:" << c.flag
                   << " head:" << c.head << " e:" << c.e << " }" ;
    }
};

// key: name,  mapped data: Country
// there may be more than one entry with the same key (many countries with the same name)
// TO DO? make the key (name) comparison case-insensitive?
using country_map = std::multimap< std::string, Country > ; // or: unordered_map

void look_up_and_print( const country_map& multimap, const std::string& name ) {

    std::cout << "\n name '" << name << "' : "  ;
    
    // https://en.cppreference.com/w/cpp/container/multimap/equal_range
    const auto range = multimap.equal_range(name) ;

    if( range.first == range.second )
        std::cout << "*** there are no countries with this name ***\n" ;

    else
    {
        std::cout << '\n' ;
        int n = 0 ;
        for( auto iter = range.first ; iter != range.second ; ++iter )
            std::cout << '\t' << ++n << ". " << iter->second << '\n' ;
    }
}

int main() {

    const country_map countries = {

        { "GGGG", { "GGGG", "hjkkd", "hhkad", GGG } },
        { "AAAA", { "AAAA", "OOO", "Ojkjs", AAA } },
        { "WWWW", { "WWWW", "Ocghaa", "Wahja", WWW } },
        { "GGGG", { "GGGG", "hjkkd2", "hhkad2", GGG } }, // duplicate key GGGG
        { "AAAA", { "AAAA", "OOO2", "Ojkjs2", AAA } }, // duplicate key AAAA
        { "FFFF", { "FFFF", "Dddaa", "Hahada", FFF } },
        { "GGGG", { "GGGG", "hjkkd3", "hhkad3", GGG } }, // duplicate key GGGG
        { "IIII", { "IIII", "Masadaa", "Iahjda", III } },
    };

    std::string name ;
    while( std::cout << "name? " && std::cin >> name ) look_up_and_print( countries, name ) ;
}

http://coliru.stacked-crooked.com/a/bf2c3de81403a826
if possible can you explain this

1
2
3
4
5
friend std::ostream& operator<< ( std::ostream& stm, const Country& c ) {

        return stm << "Country{ name:" << c.name << " flag:" << c.flag
                   << " head:" << c.head << " e:" << c.e << " }" ;
    }


I think you are overloading operator << , but i could not understand how is it doing.
which statment(s) from main function has this effect.

thank you
> I think you are overloading operator <<

Yes.

> which statment(s) from main function has this effect.

The overloaded operator is called in function look_up_and_print, line 42
std::cout << '\t' << ++n << ". " << iter->second << '\n' ;
(iter->second is the object of type Country)

Here is a simpler example:

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
#include <iostream>
#include <string>

enum e_countries { GGG=0, AAA, WWW, FFF, III };

struct Country {

    std::string name ;
    std::string flag ;
    std::string head ;
    e_countries e;
    
    // 
    friend std::ostream& operator<< ( std::ostream& stm, const Country& c ) {

        return stm << "Country{ name:" << c.name << " flag:" << c.flag
                   << " head:" << c.head << " e:" << c.e << " }" ;
    }
};

int main() {

    const Country Lithuania { "Republic of Lithuania", "Tri-colour", "Vilnius", AAA } ;

    // this calls the overloaded operator;
    // the operator is found via argument-dependent lookup
    // (an argument is of type Country; so look into the class Country for the operator)
    std::cout << Lithuania << '\n' ;
}

http://coliru.stacked-crooked.com/a/da822d7933fd46d7
Actuall, i got one more doubt here:

when i have key as string instead of int, what is the complexity to find an entry in the map?

I agree string gives unique key, but when map function find executes how does it find the entry.

iterative way or something else?

if it is using iterative way then it will be slower for large number of entries.

which is better:

{"abc", {"abc","def",20"} }

or

1
2
3
4
getkey(abc)
{
	return (a+b+c)%num;
}


above getkey() , may give duplicae keys, but complexity is only number of elements we have on duplicate key in the list, worst case if all entries have same character strings
Time complexity:

std::map look up: O( log N ) (logarithmic) where N is the size of (the number of entries in) the map.
https://en.wikipedia.org/wiki/Map_(C%2B%2B)#Performance

std::unordered_map look up: on average, O(1) (constant); worst case, O(N) (linear).
strings are compared slower than integers. So strings will be a hair slower than ints when used as a key. You should try to use an int key if you can find a way to do that.
you can roll a comparison to do 8 letters at a time (16 on some cpu that have 128 bit registers) using 64 bit ints for super large strings. Its really down in the weeds to do that, and not generally recommended. I don't know if any optimize compilers do that for you or not.

you computer can compare short strings much faster than you can compute an integer key from the letters in most cases.


Last edited on
Topic archived. No new replies allowed.