c++ overwrite [] operator with std::string [closed] - c++

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 4 years ago.
Improve this question
I'm basically trying to create a thread-safe wrapper class for std::map.
As I'm coming from C, I have quite a hard time figuring out all the nuances of C++.
I'm trying to overwrite the [] operator to take std::string arguments to just pass it to my std::map member.
By the reference of std::map::operator[] this should work fine:
T& operator[]( const Key& key );
Here is my class:
thread_map.hpp
#ifndef THREAD_MAP_H
#define THREAD_MAP_H
#include <map>
#include <functional>
#include <mutex>
template <class T>class Thread_map
{
private:
std::map<std::string, T> map;
std::mutex map_mutex;
public:
~Thread_map();
T& at(size_t pos);
T& operator[](std::string &key);
size_t size() const;
bool empty() const;
void clear();
void insert(std::pair<std::string, T> pair);
T& erase(const std::string &key);
bool for_each(std::function<bool (Thread_map, std::string&, T&)> fun);
};
template<class T> Thread_map<T>::~Thread_map()
{
this->map.clear();
}
template<class T> T& Thread_map<T>::at(size_t pos)
{
T *value;
this->map_mutex.lock();
value = this->map.at(pos);
this->map_mutex.unlock();
return value;
}
template<class T> T& Thread_map<T>::operator[](std::string &key)
{
this->map_mutex.lock();
T &value = this->map[key];
this->map_mutex.unlock();
return value;
}
template<class T> size_t Thread_map<T>::size() const
{
size_t size;
this->map_mutex.lock();
size = this->map.size();
this->map_mutex.unlock();
return size;
}
template<class T> bool Thread_map<T>::empty() const
{
bool empty;
this->map_mutex.lock();
empty = this->map.empty();
this->map_mutex.unlock();
return empty;
}
template<class T> void Thread_map<T>::clear()
{
this->map_mutex.lock();
this->map.clear();
this->map_mutex.unlock();
}
template<class T> void Thread_map<T>::insert(std::pair<std::string, T> pair)
{
this->map_mutex.lock();
this->map.insert(pair);
this->map_mutex.unlock();
}
template<class T> T& Thread_map<T>::erase(const std::string &key)
{
T *value;
this->map_mutex.lock();
value = this->map.erase(key);
this->map_mutex.unlock();
return value;
}
template<class T> bool Thread_map<T>::for_each(std::function<bool
(Thread_map, std::string&, T&)> fun)
{
}
#endif
I put the implementation into the header file, because I heard you do that with template classes. Am I right on that?
My Problem is, that when I try to call the operator
Thread_map<std::string> map;
map["mkey"] = "value";
g++ throws an invalid initialization error on map["mkey"].
As far as my understanding goes the problem is that mkey gets compiled to std::string("mkey") which is just the value, not a reference.
But why or how does the following work then?
std::map<std::string, std::string> map;
map["mkey"] = "value";
I mean I could just pass the string by value but that seems inefficient.

Reference requires a variable's address that cans be modified. For lvalue string ("some string") there is no address that can be value modified.
There are some ways that I know to solve this issue:
Remove Reference (Not recommended)
One way is to remove the '&' from the parameter. like this:
T& operator[](std::string key);
In this way you don't request lvalue, but rvalue. The problem is that when ever you'll send a value, you won't send 4 bytes of memory address, but sizeof("Your string") bytes. Heavy method..
Make a const lvalue (Recommended way)
The most beautiful way to solve this issue, is to make the parameter const lvalue (what that called rvalue reference), to make a promise to the compiler that you won't try to change the given address's value inside this function. It's looks like this:
T& operator[](const std::string &key);
Now you can send a lvalue string and rvalue string.
Give up
This way is not bad as the first one, but absolutely not good as the second. You can easily use your declaration:
T& operator[](std::string &key);
And when you pass a value, use another string variable to store the value, and use this variable at call time:
Thread_map<std::string> map;
string key = "mkey";
map[key] = "value";
(Don't do that.. Just as extension for the knowledge).

Related

Using const_cast for creating non-const variant of methods

Can the const_cast be used for creating non-const versions of already implemented methods? I think I saw something along these lines (with recommendation for the const method to do the actual work), but I'm not really sure how it's supposed to work.
Value& const search(key) const {
// find value with key
return value;
}
Value& search(key) {
return const_cast<Value&>(search(key));
}
If not this way, what is recommended way of creating non-const functions without code duplicity?
The easiest way to do it is with as_const from C++17:
Value& search(key) {
return const_cast<Value&>(std::as_const(*this).search(key));
}
Without it you can do this instead (or implement it yourself, it's not very hard)
Value& search(key) {
return const_cast<Value&>(static_cast<const T&>(*this).search(key));
}
Where T is the type of your class (you can have a generic solution with decltype but it gets really ugly due to decltype(*this) being a reference type).
You can take a look at the as_const implementation here or the generic cast here.
Two approaches.
First:
namespace notstd{ // backported C++17
template<class T>
T const& as_const(T& t){return t;}
template<class T>
T const&& as_const(T&& t){return t;}
}
namespace utility { // not ever in std
template<class T>
T& remove_const(T const& t){return const_cast<T&>(t);}
template<class T>
T&& remove_const(T const&& t){return const_cast<T&&>(t);}
}
then:
Value& const search(Key key) const {
// find value with key
return value;
}
Value& search(Key key) {
return utility::remove_const(notstd::as_const(*this).search(key));
}
or alternatively:
Value& const search(Key key) const {
return search(*this, key);
}
Value& search(Key key) {
return search(*this, key);
}
private:
template<class Self>
friend decltype(auto) search(Self& self, Key key){
// find value with key
}
where we delegate the work to a friend template where self is maybe-const.

How to add use a pointer to member value as a template parameter by type (not value)

My situation:
I frequently need to have a vector of structures where one field can be thought of as a Key or ID, and rather than store it expensively in a map (memory usage is very important in this app) I want to store it in a flat vector but present a map-like interface for finding elements by key.
My first solution to this problem:
template <class T, class Key, class KeyFn>
class TKeyedVector : public std::vector<T>
{
public:
const_iterator find(const Key& key) const {return std::find_if(begin(), end(), [&](const T& entry) {return keyFn(entry)==key; }); }
KeyFn keyFn;
};
struct KeyedDataEntry
{
std::string key;
int value;
struct KeyExtractor {
const std::string& operator()(const KeyedDataEntry& e) const {return e.key; };
};
};
using KeyedDataArray = TKeyedVector<KeyedDataEntry, std::string, KeyedDataEntry::KeyExtractor>;
Now this all works, but I would like to be able to remove the need for the KeyExtractor type by using the pointer to the member variable embedded into the type:
template <class T, class Key, Key T::* keyFn>
class TKeyedVector : public std::vector<T>
{
public:
const_iterator find(const Key& key) const {return std::find_if(begin(), end(), [&](const T& entry) {return keyFn(entry)==key; }); }
};
using KeyedDataArray = TKeyedVector<KeyedDataEntry, std::string, &KeyedDataEntry::key>;
However I can't get this to work. I've been looking at the implementation of std::mem_fn for clues, but I can't work out how to do it. The error I get with is something like:
warning C4353: nonstandard extension used: constant 0 as function expression. Use '__noop' function intrinsic instead
Any clues?
EDIT: sample version at http://ideone.com/Qu6TEy
Here is the start of a working solution. You don't need a special extractor object.
Note that I have encapsulated the vector. In time, you'll regret not doing this.
#include <vector>
#include <string>
template <class T, class Key, const Key& (T::*Extractor)() const>
class TKeyedVector
{
using storage = std::vector<T>;
using const_iterator = typename storage::const_iterator;
public:
decltype(auto) begin() const
{
return storage_.begin();
}
decltype(auto) end() const
{
return storage_.end();
}
const_iterator find(const Key& key) const
{
return std::find_if(begin(),
end(),
[&](const T& entry)
{
return entry.*Extractor() == key;
});
}
storage storage_;
};
struct KeyedDataEntry
{
std::string key;
int value;
const std::string& get_key() const { return key; }
};
int main()
{
TKeyedVector<KeyedDataEntry, std::string, &KeyedDataEntry::get_key> mymap;
}
But there is a problem with this idea of yours.
In order for this structure to be a map, the keys must be immutable. This argues for only returning immutable objects. This then argues immediately for simply using an unordered_set or set.
If you're going to return references to mutable objects in the underlying vector, then you may as well simply use std::find_if with a predicate to find them.
A pointer to member requires the pointer to member call syntax. (entry.*keyFn)()
C++17 will come with a standard std::invoke function to make writing such templates a bit less tiresome (it will work for all callable objects). But in the meanwhile, this is how you need to do this.

C++ operator[] overloading with template accessing boost::variant

I've this class with a map attribute which values are boost::variant.
typedef boost::variant<char, int, bool, unsigned short, float, timeval, double > MultiType;
class A {
public:
template<class T>
T& operator[](const std::string& key) {
return boost::get<T>(map_[key]);
}
template<class T>
std::string keyTypeToString(const std::string& key) {
std::stringstream ss;
ss << boost::get<T>(map_[key]);
return ss.str();
}
private:
std::map<std::string, MultiType> map_;
};
From main:
A a;
a["param"];
Compiler report this errors:
../src/main.cpp:8:25: error: no match for ‘operator[]’ in ‘a["param"]’
../src/main.cpp:8:25: note: candidate is:
../src/util/A.h:53:5: note: template T& A::operator[](const string&)
Maybe I'm missing something trivial, but I can't understand where I'm wrong..
Start with this:
template<class T>
T& get(const std::string& key) {
return boost::get<T>(map_[key]);
}
You call this like a.get<int>("hello"), where it will get the element "hello" as an int.
Next, write this:
struct pseudo_ref {
std::string const& str;
A* a;
template<typename T>
operator T&()&&{
return a->get<T>(str);
}
template<typename T>
pseudo_ref operator=( T&& t ) && {
a->get<typename std::decay<T>::type>(str) = std::forward<T>(t);
return {str, a};
}
pseudo_ref(pseudo_ref const&)=delete;
pseudo_ref& operator=(pseudo_ref const&)=delete;
pseudo_ref( std::string const& s, A* a_ ):str(s), a(a_) {}
};
then back in A:
pseudo_ref operator[](std::string const& str) {
return {str, this};
}
and we get [] that magically converts for you, so long as you assign to it/read from it using the exactly correct type.
This is somewhat dangerous, but cool.
If you want a const pseudo_ref, you need another class to represent it (with no = and operator T const& instead of operator T&).
In practice, this kind of malarkey is rarely worth it.
I wrote this in C++11, because writing it in C++03 is slightly more painful (and runs into lifetime issues with pseudo_ref -- they still exist if you have an auto&& x = a["hello"]), and less pain is good.
class A {
public:
class proxy {
friend class A;
private:
MultiType& it;
proxy(MultiType& it): it(it) {}
public:
template<typename T>
operator T&() {
return boost::get<T>(it);
}
};
proxy operator[](const std::string& key) {
return proxy(map_[key]);
}
private:
std::map<std::string, MultiType> map_;
};
EXPLANATION:
I can see that Yakk was trying similar thing. I have encapsulated the MultiType& from map_[key] in the proxy and then left the work on conversion (type-cast) operator. That's all.
Simple a[""] without assignment gets you the proxy.
double d = a["double"] will try to convert the proxy to double and thus call proxy::operator double&() (I had to test it because I was not sure if the type deduction will work as it is or will need some more work - well, it works!)
AFTERNOTE: It was not clear from the question and code provided what operations are allowed. We can modify the proxy to allow other operations or make it more readonly by changing the signature of type-conversion operator to return const T& instead.
Allowing modification leads to question: why not using MultiType& directly? (returning it from A::operator[]) And that leads to question: why class A at all?
AFTERNOTE #2: boost::variant does not have type-conversion operator and there must have been a reason for it. Think about this code:
int i = a["double"]
Runtime exception! I think that best solution would be to sub-class the MultiType and define type-conversion operator there (while checking boost::variant::which()).
ASSIGNING TO ALREADY PRESENT NAMES:
class A { ...
class proxy { ...
template<class T> proxy& operator=(const T& rhs) {
it = rhs; return *this; }
...but the above can only work if we already have some value in the map.
class A { ...
A() { map_["pi"] = 3.14; } ...
a["pi"] = 3.1415;
COMPLETE REDISIGN:
class MultiType: public boost::variant<int, double, ...> {
public:
template<class T> operator T() {
switch(which()) {
case 0: return boost::get<int>(*this);
case 1: return boost::get<double>(*this);
...
Now we can use std::map<std::string, MultiType> directly (without class A or any proxy).
template<class T>
T& operator[](const std::string& key) {
return boost::get<T>(map_[key]);
}
There's no way for the compiler to deduce T from a call like a["param"];. You'd need to specify it explicitly
a.operator[]<int>("param");
which I doubt is what you're after, but what do I know.

typeid() Object-Oriented Design Alternative

I have the following class using 3 different maps: keys are always strings, while values may be strings, integers or floats.
class MyMaps
{
public:
template<typename T> void addKey(const std::string& key);
void addValue(const std::string& key, const std::string& value);
void addValue(const std::string& key, int value);
void addValue(const std::string& key, float value);
private:
std::map<std::string, std::string> stringFields;
std::map<std::string, int> intFields;
std::map<std::string, float> floatFields;
};
The addValue() functions simply add a new pair to the related map. What I'm working on is the addKey() template function:
/** Add only a key, the related value is a default one and is specified by template parameter T. */
template<typename T>
void MyMaps::addKey(const string& key)
{
if (typeid(T) == typeid(string))
{
stringFields.insert(pair<string, string>(key, string()));
}
else if (typeid(T) == typeid(int))
{
intFields.insert(pair<string, int>(key, int()));;
}
else if (typeid(T) == typeid(float))
{
floatFields.insert(pair<string, float>(key, float()));
}
}
Basically, I'm using template and typeid() because I don't like this alternative that relies on type-within-function-name:
void MyMaps::addStringKey(const string& key)
{
stringFields.insert(pair<string, string>(key, string()));
}
void MyMaps::addIntKey(const string& key)
{
intFields.insert(pair<string, int>(key, int()));
}
void MyMaps::addFloatKey(const string& key)
{
floatFields.insert(pair<string, float>(key, float()));
}
The first addKey() version seems working, but I'm wondering if there is a more elegant solution. Maybe I'm missing some Object-Oriented design concept that could be helpful in this case?
Thanks in advance.
This is a perfect fit for template specialization:
template<>
void MyMaps::addKey<string>(const string& key)
{
stringFields.insert(pair<string, string>(key, string()));
}
template<>
void MyMaps::addKey<int>(const int& key)
{
intFields.insert(pair<string, int>(key, int()));;
}
template<>
void MyMaps::addKey<float>(const float& key)
{
floatFields.insert(pair<string, float>(key, float()));
}
EDIT: For syntax/more info about template specialization read: Template Specialization and Partial Template Specialization
Or better yet, if boost is an option and if the keys are unique for all 3 maps and you have 3 different maps just to be able to store them, then consider using boost::variant:
typedef boost::variant<string, int, float> ValueType;
class MyMap
{
public:
typedef std::map<std::string, ValueType> MapType;
template<typename T> void addKey(const std::string& key, T &val)
{
ValueType varVal= val;
allFields.insert(MapType::value_type(key, varVal));
}
private:
MapType allFields;
};
Your question inquires 2 things:
The real question, crafting a key value Map or Dictionary, using different types, for the values, in the same collection.
And, a potential solution, applying the "typeid" function.
More reference about "typeid":
http://en.cppreference.com/w/cpp/language/typeid
Object (and Class) Orientation is great, but, sometimes you may want to mix it with other paradigms.
What about "pointers" ?
Pointers allow different types, to be treated as the same simple type.
What about a Key Value Dictionary Collection, that stores a string key, and a pointer value, and the pointer may be integer, string, or object.
Or to be more specific. the "Value" may be a tuple, where the first field (maybe an enumerated type), indicates the real type of the value. And, the second field of the "Value" is a pointer or variant to the real field.
The first suggestion using "Unions" (a.k.a. "variants"), no pointers:
#include <string>
#include <typeinfo>
union ValueUnion
{
int AsInt,
float AsFloat,
std::string& AsStr
};
struct ValueType
{
std::type_info Id,
ValueUnion Value
};
class MyMaps
{
public:
template<typename T> void addKey(const std::string& key);
void addValue(const std::string& key, const std::string& value);
void addValue(const std::string& key, int value);
void addValue(const std::string& key, float value);
private:
std::map<std::string, ValueType> Fields;
};
Or, with pointers:
#include <string>
#include <typeinfo>
struct ValueType
{
std::type_info Id,
void* Value
};
class MyMaps
{
public:
template<typename T> void addKey(const std::string& key);
void addValue(const std::string& key, const std::string& value);
void addValue(const std::string& key, int value);
void addValue(const std::string& key, float value);
private:
std::map<std::string, ValueType> Fields;
};
I have seen this "pattern" several times, I called the "Key-Value-Type" collection.
Note: Not many experience with the STL, are you sure "std::map",
is the right collection ?
Just my 2 cents.
Might not be what you want because it's a different approach but you could use a map of variants. you can define a boost::variant to hold only string,int or float.
eladidan beat me to it and I don't know how to delete answers.

Pointer to member function in template class

I want to eliminate the process consumed by a decision using pointers to member functions. I need to give user options to turn on or off domain checking for a function defined on a limited continues domain.
It's ok to have pointers to member functions when not using templates. But here I have to generalize my implementation.
Specifically I have three member functions in my class:
1.value is a member function returns value calculated by the member that function points to. The function is a function pointer wich points to either checkedValue or uncheckedValue.
2.checkedValue is a member function that calculates and returns the result, if input is in specified range. else throws a std::domain error.
3.uncheckedValue calculates and returns the result, regardless of domain.
template <typename T>
class IO
{
private:
typedef T (IO<T>::*functionPtr)(const std::string& name, const T& input) const;
functionPtr function;
bool domainValidation;
void setDomainValidation(const bool& value);
//...
public:
// ...
T value(const std::string& name, const T& input) const;
T uncheckedValue(const std::string& name, const T& input) const;
T checkedValue(const size_t& index, const T &input) const;
};
// Implementation:
template <typename T>
void IO<T>::setDomainValidation(const bool &value)
{
domainValidation = value;
if(domainValidation)
{
// function points to checkedValue()
function = & IO<T>::checkedValue; // Here I got undefinded reference error
}
else
{
// function points to uncheckedValue()
}
}
template <typename T>
T IO<T>::value(const string &name, const T &input) const
{
return (this->*function)(name,input);
}
template <typename T>
T IO<T>::uncheckedValue(const string &name, const T &input) const
{
// simply calculate and return the result
}
template <typename T>
T IO<T>::checkedValue(const string &name, const T &input) const
{
// if input is in domain, calculate and return the result
// else throw a std::domain error
}
This looks like a typo to me: The function signature of the function pointer is
...(const std::string &, const T &) ...
The signature of checkedValue is
...(const size_t &, const & T) ...
If the signature is changed to match the function pointer, the code sample compiles for me. Concerning the performance: Are you sure (as in have profiled or looked at the assembly) that the simple if-clause inside the value-method is worse than the (possible) indirection through a function pointer? Obviousl, the call to checked or uncheckedValue cannot be inlined.
Your function has signature
T checkedValue(const size_t& index, const T &input) const;
but not
T IO<T>::checkedValue(const string &name, const T &input) const;
Note the difference between the types of the first parameter.