This code compiles successfully with VS, but when porting it to Linux and compiling with g++ it errors out.
template <class Key, class Value>
void Dictionary<Key, Value>::set(const Key &key, Value value)
{
typename Dictionary<Key,Value>::iterator i = this->find(key);
if (i == this->end())
{
access(&key, &value, eINSERT);
this->insert(value_type(key, value));
}
else
{
access(&key, &i->second, eCLEAR);
dispose(i->second);
access(&key, &value, eSET);
i->second = value;
}
}
template <class Key, class Value> class PtrDictionary : public Dictionary<Key, Value>;
class Processor
{
private:
ptrdictionary<const string,const type*> m_Types;
void Processor::add(const string name, const Type* t)
{
if (m_Types.get(name))
error("Type '"+string(name)+"' already defined", eParseError);
m_Types.set(name, t); // this is where the error is received
}
}
The error is:
../Packages/Dictionary.h: In instantiation of 'void Dictionary::set(const Key&, Value) [with Key = const
std::basic_string; Value = const AsmLoader::Type*]':
Instructions.cpp:94:21: required from here
../Packages/Dictionary.h:79:37: error: expected primary-expression
this->insert(value_type(key, value));
Update:
value_type is an object in std::map, and all it took for g++ to compile the code was to add this:
typedef typename map<const Key, Value>::value_type value_type;
Thanks everyone for your comments, this is my first time using SO and this is a very complex code so I forgot to specify this part...
Unfortunately, it does not look as if this can be implemented in the same fashion as in VS. In researching this, I found:
Can dictionaries be used in c++
They suggested using std::map in STL as a similar method in C++ (without .NET). In case the above link changes, here is a quote from the answer supplied:
There is a corresponding type in STL, that's called std::map.
It has the same basic functionality as a .NET Dictionary, but the
implementation is quite different. std::map is internally based on a
red-black tree datastructure, while Dictionary uses a hash table
internally.
If you're just looking for something with the same behaviour, std::map
will do, but if you have large amounts of data you have to be aware of
the different performance characteristics.
** Caveat - I am still learning, so apologies if this is off the mark of your original question. Hope this helps!
Resolved!
value_type is an object in std::map, and all it took for g++ to compile the code was to add this:
typedef typename map<const Key, Value>::value_type value_type;
Related
I am a beginner of C++, and I code what The Cherno teaches in his 100th video of the C++ series, showing at 16:50. But VS is always giving me error.
If without commenting the const part, VS gives me error c3848. After adding const, VS give me error c2676.
I have checked the way to use std::hash on cppreference, and searched for the error on Google, but get nothing. It's just "a little bit" too hard for a beginner like me.
Below is the code.
#include<iostream>
#include<map>
#include<unordered_map>
struct CityRecord
{
std::string Name;
uint64_t Population;
double Latitude, Longtitude;
};
namespace std {
template<>
struct hash<CityRecord>
{
size_t operator()(const CityRecord& key) //const noexcept
{
return hash<std::string>()(key.Name);
}
};
}
int main()
{
std::unordered_map<CityRecord, uint32_t> foundedMap;
foundedMap[CityRecord{ "London", 500000, 2.4, 9.4 }] = 1850;
uint32_t NewYorkYear = foundedMap[CityRecord{ "NY", 7000000, 2.4, 9.4 }];
}
As a beginner, I just want to know how to use the hash function in this case.
There is a much easier solution, without opening the std namespace and specializing the std::hash
If you look at the definition of the std::unordered_map in the CPP reference here, then you will read:
template<
class Key,
class T,
class Hash = std::hash<Key>,
class KeyEqual = std::equal_to<Key>,
class Allocator = std::allocator< std::pair<const Key, T> >
> class unordered_map;
It is clear and normal, to hand in template parameters for the key-type and the value-type value. However, if the key-type if a custom type, like in your case, then you need to add additional functionality.
First, you need to add hash functionionality. If you read here about std::hash, then the only function the will be called is the "function call operator".
And this must be a "const" function, which will fix one of your problems.
And of course, you may add this function to your struct. That is completely OK. Please see in the example code below. With taht, you can give your own class as a template parameter for the hash functionality to the std::unordered_map. Cool.
If we look at the next template parameter of the std::unordered_map, then we will find std::equal_to. And if we read about this in cppreference, then we will find the following statement:
Function object for performing comparisons. Unless specialised, invokes operator== on type T.
So, we need to add a comparison operator == to your custom struct to satisfy requirements.
Please note: It is a good approach to encapsulate the methods operating on the data of classes within the class and not define them as free functions. Because only the methods of the class should work on the data of the class. If you would later change something in a class and have a free function doing work on the class members. So, please try to follow that approach.
Then, next, the comparison. So, we define the "operator ==" in your class and then have to compare element by element.
For easing up this task, there is a library function called std::tie. Please see here. This basically creates a std::tuple from the given parameters with the advantage, that all comparison functions are already defined and can be immediately reused.
By following the above described approach, the whole implementation will be much simpler.
Please see the below example code:
#include<iostream>
#include<map>
#include<unordered_map>
#include<tuple>
struct CityRecord
{
std::string Name;
uint64_t Population;
double Latitude, Longtitude;
// For std::equal_to
bool operator == (const CityRecord& cr) const { return std::tie(Name, Population, Latitude, Longtitude) == std::tie(cr.Name, cr.Population, cr.Latitude, cr.Longtitude); }
// For hashing
size_t operator()(const CityRecord& key) const { return std::hash<std::string>{}(key.Name); }
};
int main() {
// Definition of the unordered_map
std::unordered_map<CityRecord, uint32_t, CityRecord> foundedMap;
// Adding data
foundedMap[CityRecord{ "London", 500000, 2.4, 9.4 }] = 1850;
uint32_t NewYorkYear = foundedMap[CityRecord{ "NY", 7000000, 2.4, 9.4 }];
}
You need to make the overloaded operator() for the specialization of hash for CityRecord a const member function as shown below. Additionally, we also need to overload operator== for CityRecord as shown below:
struct CityRecord
{
std::string Name;
uint64_t Population;
double Latitude, Longtitude;
//friend declaration for operator== not needed since we've a struct
};
//implement operator==
bool operator==(const CityRecord &lhs, const CityRecord &rhs)
{
return (lhs.Name == rhs.Name) && (lhs.Population == rhs.Population) && (lhs.Latitude ==rhs.Latitude) && (lhs.Longtitude == rhs.Longtitude);
}
namespace std {
template<>
struct hash<CityRecord>
{
//-----------------------------------------------vvvvv-->added this const
size_t operator()(const CityRecord& key) const
{
return hash<std::string>()(key.Name) ^ hash<uint64_t>()(key.Population) ^ hash<double>()(key.Latitude) ^ hash<double>()(key.Longtitude);
}
};
}
Working demo
Here we use an (unnamed) hash<string> object to generate a hash
code for Name, an object of type hash<uint64_t> to generate a hash from Population, and an object of type hash to generate a hash from Latitute and finally an object of type hash<double> to generate a hash from Longitute. Next, we exclusive OR these results to form an overall hash code for the given CityRecord object.
Note that we defined our hash function to hash all the four data members so that our hash function will be compatible with our definition of operator== for CityRecord.
Iterator of each datastructure has different type of return value of operator*() e.g. :-
Most array-like DataStructure<T> : return T&.
std::unordered_map<K,T> : return std::pair<const K, T>&.
const std::unordered_map<K,T> : return const std::pair<K, T>&.
What should be the datatype if my iterator::operator*() want to return something more complex (std::pair is not enough)?
Example
I have a custom 3D datastructure Map3D<T> that is like a map (int,int,int)->T.
To achieve the above requirement, I create a new class Bundle3D<T>.
template<class T>class Bundle3D {
//getInternalIndex1D()
//getCoordinate()
//getValue()
};
The Map3D<T>'s iterator::operator* would have signature like :-
Bundle3D<T> iterator::operator*(){ ... }
Its usage is like :-
Map3D<float> map;
for(auto& ele:map){ //"ele" has some special type named "Bundle"
int keyInternal1DIndex=ele.getInternalIndex1D();
Vector3Int& key=ele.getCoordinate();
float& value=ele.getValue();
}
It works good, but I think my code is not standardized.
In other words, Bundle3D<T>, getInternalIndex1D(), getCoordinate() and getValue() is blindly named by me.
In real case, I have created a lot of custom data-structures that generate such strange iterator.
Question
Is there any std::/standard type of return-value of iterator::operator*(), when T& and std::pair is not enough?
I have doubted about this for several months, but feel very reluctant to ask it.
If this question need improvement, please comment.
Edit
(clarify the word standardized - this part is my subjective notion.)
I feel that all types of standard collection in most language, e.g. :-
java.util.Collection/ArrayList in Java
std::unordered_map/vector in C++
Iterators of all of them have signatures of function getValue() or operator* that return either T or StandardPair<index,T> - no other types.
For many years, I am convinced that it is a rule or a strong convention/tradition that I really should obey.
There must be a reason.
However, I am not sure what it is, because I am probably very new to C++.
If I design my software to return something strange (e.g. Bundle3D<T> in the above example), I think I will get punished hard from the unconventional design. (e.g. not have .first field)
What you have right now is okay. I would just specify one thing that I think isn't very C++-ish and may in fact harm your standardization options in the future.
It's the getters. Obviously you have deep roots in Java, and the concept of public members is abhorred in Java programming, so much so that the concept of "beans" exists. I don't intend to knock on Java here, it's a nice language with its own nice idioms.
But this is C++, with it's own programming idioms. You obviously noticed the direct access to the contents of std::pair. It's like that for a reason. A pair is just two items packed together, that's the sum of its behavior. Getters would just ,well, get in the way. :)
There's no need for them, because we aren't dealing with an "abstraction" of two items bundled together, but instead we really do have a concrete, honest to god, bundle1.
Obviously we can have bundles of more than two items. That's why we have std::tuple. Now while it's true that all access to a tuple is through a non-member function called get, that's simply because there is no way to give names to members of an arbitrarily sized bundle. get still returns a reference to the element, so you retain direct access into the bundle.
This long winded exposition is important, because an upcoming C++1z feature, called "Structured Bindings", relies on the standard behavior of tuples and pairs, as well as how C++ programmers see aggregates. Under this feature, an iteration over a map, will look like this:
#include <map>
#include <iostream>
std::map<char const*, int> foo()
{
return {
{ "foo", 3 },
{ "bar", 7 },
{ "baz", 1 },
};
}
int main() {
for (auto [key, val] : foo()) {
std:: cout << "( " << key << ", " << val << " )\n";
}
return 0;
}
Live example
And the same extends to any user defined bundle of data. Your own return value type in the post can be bound similarly, if the members are public:
struct Vector3Int {};
template<class T>
struct Bundle3D {
int internal_index_id;
Vector3Int const &coord;
T &value;
};
int main() {
Vector3Int vec;
float val;
Bundle3D<float> bundle{ 1, vec, val };
auto[ idx_id, coord, value] = bundle;
// coord = {}; // compile error since coord gets the cv qualifer
}
Live example
So my suggestion is, leave the getters to your Java code, and use aggregates as bundles of data for C++.
1 An aggregate, to be more formal.
I'm going to assume that your Map3D is a spacial co-ordinate to value container.
I would return a std::pair<const Vector3Int, T>&, and not bother with getInternalIndex1D as part of the return. That can be left as a function Vector3Int -> int.
Have a look at UnorderedAssociativeContainer for the sorts of members that would be useful to define
e.g.
template <typename T>
class Map3D
{
using key_type = Vector3Int;
using mapped_type = T;
using value_type = Bundle3D<T>; // Key, Value, Internal1DIndex
using reference = Bundle3D<T> &;
using pointer = Bundle3D<T> *;
using hasher = /*Function object that calculates Internal1DIndex*/
using key_equal = std::equal<Vector3Int>
... iterators, const_* definitions, etc
}
The Boost documentation doesn't elaborate much, but there is an (optional) KeyCompare function that can be passed to the ptree.
Anyone have a good example of using a custom KeyCompare function?
I have recently been working with a ptree that is real slow. My keys are long strings (paths), and I assuming it's the string comparisons that make it slow.
From what I can glean, the default KeyCompare is std::less(), I want to change this. I think something that just compares the hashes of the two strings.
It goes without saying (but I'll say it anyway) that I would use a different object for the key to facilitate this: Something that has (std::string+hash), rather than just a std::string. The hash would be calculated during construction.
Thanks,
Rik.
Found this from the boost source code: An example of a case-insensitive KeyCompare:
template<class T>
struct less_nocase
{
typedef typename T::value_type Ch;
std::locale m_locale;
inline bool operator()(Ch c1, Ch c2) const
{
return std::toupper(c1, m_locale) < std::toupper(c2, m_locale);
}
inline bool operator()(const T &t1, const T &t2) const
{
return std::lexicographical_compare(t1.begin(), t1.end(),
t2.begin(), t2.end(), *this);
}
};
Then all you need to do is pass it in to the basic_ptree class:
typedef basic_ptree<std::string, std::string,
less_nocase<std::string> > iptree;
Problem: I want disparate sections of my code to be able to access a common collection that stores objects of different types in such a way that the type of each object is known and, crucially, retrieval from the collection should be type checked at compile time. (I realise this is close to questions asked before, but please read on, this is somewhat more specific.)
To give a concrete example, I would like something that does the following:
// Stuff that can go in the collection:
enum Key { NUM_APPLES /* (unsigned int) */, APPLE_SIZE /* (double) */ }
map<Key, Something> collection;
unsigned int * nApples = collection.find(NUM_APPLES);
int * appleSize = collection.find(APPLE_SIZE); // COMPILATION ERROR - WRONG TYPE
My solution: So far I have devised the following solution using boost::any:
The key:
using namespace std;
using namespace boost::any;
struct KeySupertype
{
protected:
// Can't create an instance of this type
KeySupertype() {}
private:
// Can't copy
KeySupertype& operator = (const KeySupertype& other) {}
KeySupertype(const KeySupertype& other) {}
};
template <typename Type>
struct Key : public KeySupertype
{
public:
Key() {}
};
The collection:
class PropertiesMap
{
public:
template<typename T>
T * find(Key<T> & key);
/* Skipping erase, insert and other methods for brevity. */
private:
map<const KeySupertype *, any> myAnyMap;
};
template <typename T>
T * PropertiesMap::find(Key<T> & key)
{
const map<const KeySupertype *, any>::iterator it = myAnyMap.find(&key);
if(it == myAnyMap.end())
return NULL;
return any_cast<T>(&it->second);
}
Usage:
static const Key<unsigned int> NUM_APPLES;
static const Key<double> APPLE_SIZE;
PropertiesMap collection;
/* ...insert num apples and apple size into collection ...*/
unsigned int * const nApples = collection.find(NUM_APPLES);
int * const nApples = collection.find(NUM_APPLES); // COMPILATION ERROR
This way type information is encoded with each Key according to its template parameter so the type will be enforced when interacting with the collection.
Questions:
1) Is this a reasonable way to achieve my goal?
2) A point of nastyness is that the collection uses the address of Key objects as the internal std::map key. Is there a way around this? Or at least a way to mitigate misuse? I've tried using a unique int in each Key that was generated from a static int (and making the std::map key type an int), but I'd like to avoid statics if possible for threading reasons.
3) To avoid using boost::any would it be reasonable to have the std::map be of type <const KeySupertype *, void *> and use a static_cast<T> instead of any_cast?
1) Looks nice to me, a clever solution
2) I guess you're afraid that someone will copy the key and its address will change. If that's your concern, keep an "original address" field in your KeySuperType. During construction set the original address to this, during copying set the original address to the original address of the right hand (source). Use this original address to access the map contents. I really couldn't think of a compile time solution to this, since at compile time, compilation units will not know about each other. You could assign unique ID's to the keys earliest in link time, and getting the address of global variables is sort of equivalent to that. The only weak point I can see with this solution is if you define the same key in two dynamic shared libraries without extern, they'll silently have their own versions of the key with different addresses. Of course, if everything goes into the same binary, you won't have that problem, since two declarations without extern will cause a "Multiple Declaration" linker error.
3) If your problem with boost::any is depending on boost (which is more OK then you think), then implement any yourself, it's surprisingly simple. If the problem is performance, then static_cast<> also seems OK to me, as long as you keep the internals of your PropertiesMap
away from those that don't know what they're doing...
Hi I have a problem to compile my class in XCode, gcc(Apple LLVM compiler 3.0)
I wrote class ContextSchedule it means class which encapsulates list of other class member functions and have no problem to compile it under MSVC++ 2005.
template<class T>
class C_ContextScheduler
{
public:
typedef void (T::*T_EventFunc)();
typedef std::map<u64, T_EventFunc> T_EventMap;
public:
//# c-tor
C_ContextScheduler(T & context) : m_Context(context), m_currentTick(0) {};
//# Schedule
//# funcPtr - pointer to function of class T
//# dellayTime in milliseconds - after dellayTime from now will be funcPtr called
void Schedule(T_EventFunc funcPtr, u32 dellayTime)
{
u64 callingTime = m_currentTick + dellayTime;
std::pair<int, bool> res = m_eventMap.insert(T_EventMap::value_type(callingTime, funcPtr));
SC_ASSERT(res.second);
} ...
Any ideas? Want preserve template way of this solution, thnx.
When the compiler compiles this template, T is not yet known. Therefore the exact type of T_EventFunc and T_EventMap is also not yet known and that compiler doesn't know that T_EventMap::value_type will end up being a type. To make this clear, use the typename keyword:
... = m_eventMap.insert(typename T_EventMap::value_type(callingTime, funcPtr));
Since you don't supply the error you are getting, we can only guess. And my guess is that the result of your insert-call is not correct.
According to this refernce, the return value of std::map::insert is std::pair<iterator, bool>. Are you sure the iterator is an int?