Tuples are kind of like structs. Are there also tuples that behave like unions? Or unions where I can access the members like in tuples, e.g.
my_union_tuple<int, char> u;
get<1>(u);
get<int>(u); // C++14 only, or see below
For the 2nd line, see here.
Of course, the solution should not only work for a specific union, like <int, char>, but for arbitrary types and number of types.
No std::tuple<A, B> means A AND B.
If you want a typesafe union-like container, have a look to boost variant.
boost::variant<int, std::string> v;
v = "hello";
std::cout << v << std::endl;
It does provide safe traversing with visitors:
class times_two_visitor
: public boost::static_visitor<>
{
public:
void operator()(int & i) const
{
i *= 2;
}
void operator()(std::string & str) const
{
str += str;
}
};
Or even direct accessors that can throw if the type is not good:
std::string& str = boost::get<std::string>(v);
(Code taken from boost variant basic tutorial)
Related
I am currently using a map with string data type as key and a variant value. I am relying on the boost::variant library for defining the map data type. I am currently following a few tutorial on boost::variant for extracting the values from the map without having to specifying the exact data type value I want to retrieve while iterating through it.
source: https://theboostcpplibraries.com/boost.variant
I want something like the last example for generic data type but instead of printing the values I was to return them.
The following below does not seem to be working correctly.
struct output : public boost::static_visitor<>
{
template <typename T>
T operator()(T t) const { return t }
};
I also instantiate a map while populating it with a string data type as keys and boost::variant type as values; and later on iterate through the map and process the values.
std::map<std::string,boost::variant<int, double, long long, std::string>> _keyPairValue;
for(auto &x: _keyPairValue)
{
// appending a key pair value to a bson object which is part of mongo sdk
b << x.first << boost::apply_visitor(output{},x.second);
}
I want something like the last example for generic data type but instead of printing the values I was to return them.
You probably want to have operator() overloads, something like:
struct process : public boost::static_visitor<>
{
void operator()(double d)
{
// process d ...
}
void operator()(char c)
{
// process c ...
}
void operator()(const string& s)
{
// process s ...
}
};
int main()
{
boost::variant<double, char, std::string> v;
v = 3.14;
boost::apply_visitor(process{}, v);
v = 'A';
boost::apply_visitor(process{}, v);
v = "Boost";
boost::apply_visitor(process{}, v);
}
I'm trying to use std::variant as a class member variable and then use operator overloading so that the two Variants of this class can use the operator plus to produce a new variable. The problem is that std::get does not work as I thought and so I cannot retrieve the correct (hardcoded) string types so that the AddVisitor struct is used.
I get a compilation error that says: no matching function for call to ‘get<0>(std::basic_string&)’
Also is there a way that operator+ function detects the type without if-else statements?
I have already checked a lot of answers in SO including ones that answer questions about similar Boost functionality, but I cannot get it to work.
#include <iostream>
#include <variant>
#include <string>
#include "stdafx.h"
using Variant = std::variant<int, std::string>;
template<typename T>
struct AddVisitor
{
T operator()(T v1, T v2)
{
return v1 + v2;
}
};
class Var
{
Variant v;
public:
template<typename T>
Var(T value) : v(value) {}
Var operator+(Var& val)
{
// PROBLEM: This is a hard coded example that I want to use, so that concatenation of two strings happens.
return std::visit(AddVisitor<std::string>(), std::get<std::string>(v), std::get<std::string>(val.get()));
// Is there a way to get the correct type without if-else statements here?
}
Variant get()
{
return v;
}
};
int main()
{
Var x("Hello "), y("World");
// The expected output is this:
Var res = x + y;
return 0;
}
I expect to be able to use the plus operator and concatenate two strings or two integers and create a new Var variable.
Ok, so there are a few things to talk about.
First, the visitor for std::visit with more than one variant argument should accept all combinations of variant types. In your case it should accept:
(string, string)
(string, int)
(int, int)
(int, string)
If for you only string, string and int, int are valid you still need to accept the other combinations for the code to compile, but you can throw in them.
Next, the visitor shouldn't be templated. Instead the operator() should be templated or overloaded for all the above combinations.
So here is AddVisitor:
struct AddVisitor
{
auto operator()(const std::string& a, const std::string& b) const -> Variant
{
return a + b;
}
auto operator()(int a, int b) const -> Variant
{
return a + b;
}
// all other overloads invalid
template <class T, class U>
auto operator()(T, U) const -> Variant
{
throw std::invalid_argument{"invalid"};
}
};
It's not clear from documentation what the overloads can return, but I couldn't make it compile unless all return Variant. Fortunately the compiler errors are TREMENDOUSLY HELPFULL . (I need to check the standard).
Next, when you call std::visit you need to pass the variants you have.
So the final code is this:
auto operator+(Var& val) -> Var
{
return std::visit(AddVisitor{}, get(), val.get());
}
And you can indeed use it like you want:
Var res = x + y;
Another issue with your code is that get makes unnecessary copies. And copies of std::variant are not cheap to make. So I suggest:
auto get() const -> const Variant& { return v; }
auto get() -> Variant& { return v; }
I have two different objects:
struct TypeA {
std::size_t no;
std::string data;
std::string data2;
};
struct TypeB {
std::size_t no;
std::string data;
std::string data2;
std::string data3;
};
They are stored in a std::vector with std::variant
std::vector<std::variant< TypeA, TypeB>> ab;
Now i want to remove all elements were the member no = 0.
Without the std::variant with the vector containing only TypeA I would do it like this:
ab.erase(std::remove_if(ab.begin(), ab.end(),
[](const TypeA& a) { return a.no == 0; }), ab.end());
But how to incorporate the std::variant ? I tried to come up with something with std::visit but i cannot ad it in the predicate of std::remove_if or can I?
Yes, std::visit can help. The functor passed to visit just needs to be able to accept each type of the variant, and the easiest way to do that is with a generic lambda:
ab.erase(
std::remove_if(
ab.begin(),
ab.end(),
[](const auto &v) {
return std::visit(
[](const auto &obj) { return obj.no == 0; },
v);
}),
ab.end());
Here the type of v for the outer lambda is always used as const std::variant<TypeA, TypeB>&, and auto is just more convenient than typing out std::variant<TypeA, TypeB>. But for the inner lambda, it's important that the lambda is generic, because visit will instantiate its template operator() with both TypeA and TypeB.
If you want to access the "same" data member of different types, then these types need to be subclasses of a common polymorphic base class defining this data member.
In your case, however, where TypeA and TypeB are not related, you'll have to make a type-safe access to the respective data member. The solution provided by #aschepler shows this in a generic way using std::visit functor; the following solution is without std::visit (hence not that elegant, but still working):
ab.erase(std::remove_if(ab.begin(), ab.end(),
[](const std::variant< TypeA, TypeB>& v) {
int no;
if (v.index()==0) {
no = std::get<0>(v).no;
} else {
no = std::get<1>(v).no;
}
return no==0;
}), ab.end());
I have an unordered_map that uses a string-type as a key:
std::unordered_map<string, value> map;
A std::hash specialization is provided for string, as well as a
suitable operator==.
Now I also have a "string view" class, which is a weak pointer into an existing string, avoiding heap allocations:
class string_view {
string *data;
size_t begin, len;
// ...
};
Now I'd like to be able to check if a key exists in the map using a string_view object. Unfortunately, std::unordered_map::find takes a Key argument, not a generic T argument.
(Sure, I can "promote" one to a string, but that causes an allocation I'd like to avoid.)
What I would've liked instead was something like
template<class Key, class Value>
class unordered_map
{
template<class T> iterator find(const T &t);
};
which would require operator==(T, Key) and std::hash<T>() to be suitably defined, and would return an iterator to a matching value.
Is there any workaround?
P0919R2 Heterogeneous lookup for unordered containers has been merged in the C++2a's working draft!
The abstract seems a perfect match w.r.t. my original question :-)
Abstract
This proposal adds heterogeneous lookup support to the unordered associative containers in the C++ Standard Library. As a result, a creation of a temporary key object is not needed when different (but compatible) type is provided as a key to the member function. This also makes unordered and regular associative container interfaces and functionality more compatible with each other.
With the changes proposed by this paper the following code will work without any additional performance hits:
template<typename Key, typename Value>
using h_str_umap = std::unordered_map<Key, Value, string_hash>;
h_str_umap<std::string, int> map = /* ... */;
map.find("This does not create a temporary std::string object :-)"sv);
As mentioned above, C++14 does not provide heterogeneous lookup for std::unordered_map (unlike std::map). You can use Boost.MultiIndex to define a fairly close substitute for std::unordered_map that allows you to look up string_views without allocating temporary std::strings:
Live Coliru Demo
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/hashed_index.hpp>
#include <boost/multi_index/member.hpp>
#include <string>
using namespace boost::multi_index;
struct string_view
{
std::string *data;
std::size_t begin,len;
};
template<typename T,typename Q>
struct mutable_pair
{
T first;
mutable Q second;
};
struct string_view_hash
{
std::size_t operator()(const string_view& v)const
{
return boost::hash_range(
v.data->begin()+v.begin,v.data->begin()+v.begin+v.len);
}
std::size_t operator()(const std::string& s)const
{
return boost::hash_range(s.begin(),s.end());
}
};
struct string_view_equal_to
{
std::size_t operator()(const std::string& s1,const std::string& s2)const
{
return s1==s2;
}
std::size_t operator()(const std::string& s1,const string_view& v2)const
{
return s1.size()==v2.len&&
std::equal(
s1.begin(),s1.end(),
v2.data->begin()+v2.begin);
}
std::size_t operator()(const string_view& v1,const std::string& s2)const
{
return v1.len==s2.size()&&
std::equal(
v1.data->begin()+v1.begin,v1.data->begin()+v1.begin+v1.len,
s2.begin());
}
};
template<typename Q>
using unordered_string_map=multi_index_container<
mutable_pair<std::string,Q>,
indexed_by<
hashed_unique<
member<
mutable_pair<std::string,Q>,
std::string,
&mutable_pair<std::string,Q>::first
>,
string_view_hash,
string_view_equal_to
>
>
>;
#include <iostream>
int main()
{
unordered_string_map<int> m={{"hello",0},{"boost",1},{"bye",2}};
std::string str="helloboost";
auto it=m.find(string_view{&str,5,5});
std::cout<<it->first<<","<<it->second<<"\n";
}
Output
boost,1
I faced an equal problem.
We need two structs:
struct string_equal {
using is_transparent = std::true_type ;
bool operator()(std::string_view l, std::string_view r) const noexcept
{
return l == r;
}
};
struct string_hash {
using is_transparent = std::true_type ;
auto operator()(std::string_view str) const noexcept {
return std::hash<std::string_view>()(str);
}
};
For unordered_map:
template <typename Value>
using string_unorderd_map = std::unordered_map<std::string, Value, string_hash, string_equal>;
For unordered_set:
using string_unorderd_set = std::unordered_set<std::string, string_hash, string_equal>;
Now using string_view is possible.
It looks like only as recently as C++14 did even the basic map get such a templated find for is_transparent types in the comparison. Most likely the correct implementation for hashed containers was not immediately evident.
As far as I can see your two options are:
Just do the allocation and profile to see if maybe it's not actually a problem.
Take a look at boost::multi_index (http://www.boost.org/doc/libs/1_60_0/libs/multi_index/doc/index.html) and have both string and string_view indexes into the container.
This solution has drawbacks, which may or may not make it unviable for your context.
You can make a wrapper class:
struct str_wrapper {
const char* start, end;
};
And change your map to use str_wrapper as its key. You'd have to add 2 constructors to str_wrapper, one for std::string and one for your string_view. The major decision is whether to make these constructors perform deep or shallow copies.
For example, if you use std::string only for inserts and str_view only for lookups, you'd make the std::string constructor deep and the str_view one shallow (this can be enforced at compile time if you use a custom wrapper around unordered_map). If you care to avoid memory leaks on the deep copy you would need additional fields to support proper destruction.
If your usage is more varied, (looking up std::string's or inserting by str_view), there will be drawbacks, which again, might make the approach too distasteful so as to be unviable. It depends on your intended usage.
Yet another option is to split the lookup and the data management by using multiple containters:
std::unordered_map<string_view, value> map;
std::vector<unique_ptr<const char[]>> mapKeyStore;
Lookups are done using string_views without the need of allocations.
Whenever a new key is inserted we need to add a real string allocation first:
mapKeyStore.push_back(conv(str)); // str can be string_view, char*, string... as long as it converts to unique_ptr<const char[]> or whatever type
map.emplace(mapKeyStore.back().get(), value)
It would be much more intuitive to use std::string in the mapKeyStore. However, using std::string does not guarantee unchanging string memory (e.g. if the vector resizes). With the unique_ptr this is enforced. However, we need some special conversion/allocation routine, called conv in the example. If you have a custom string container which guarantees data consistency under moves (and forces the vector to use moves), then you can use it here.
The drawback
The disadvantage of the above method is that handling deletions is non-trivial and expensive if done naive. If the map is only created once or only growing this is a non-issue and the above pattern works quite well.
Running example
The below example includes a naive deletion of one key.
#include <vector>
#include <unordered_map>
#include <string>
#include <string_view>
#include <iostream>
#include <memory>
#include <algorithm>
using namespace std;
using PayLoad = int;
unique_ptr<const char[]> conv(string_view str) {
unique_ptr<char[]> p (new char [str.size()+1]);
memcpy(p.get(), str.data(), str.size()+1);
return move(p);
}
int main() {
unordered_map<string_view, PayLoad> map;
vector<unique_ptr<const char[]>> mapKeyStore;
// Add multiple values
mapKeyStore.push_back(conv("a"));
map.emplace(mapKeyStore.back().get(), 3);
mapKeyStore.push_back(conv("b"));
map.emplace(mapKeyStore.back().get(), 1);
mapKeyStore.push_back(conv("c"));
map.emplace(mapKeyStore.back().get(), 4);
// Search all keys
cout << map.find("a")->second;
cout << map.find("b")->second;
cout << map.find("c")->second;
// Delete the "a" key
map.erase("a");
mapKeyStore.erase(remove_if(mapKeyStore.begin(), mapKeyStore.end(),
[](const auto& a){ return strcmp(a.get(), "a") == 0; }),
mapKeyStore.end());
// Test if verything is OK.
cout << '\n';
for(auto it : map)
cout << it.first << ": " << it.second << "\n";
return 0;
}
Of course, the two containers can be put into a wrapper which handles the insertion and deletion for its own.
I'll just present one variation I found on github, it involves defining a new map class that wraps the std.
Redefining some key API to intercept the adaptors we want, and use a static string to copy the key.
It's not necessary a good solution, but it's interesting to know it exists for people who deems it enough.
original:
https://gist.github.com/facontidavide/95f20c28df8ec91729f9d8ab01e7d2df
code gist:
template <typename Value>
class StringMap: public std::unordered_map<std::string, Value>
{
public:
typename std::unordered_map<string,Value>::iterator find(const nonstd::string_view& v )
{
tmp_.reserve( v.size() );
tmp_.assign( v.data(), v.size() );
return std::unordered_map<string, Value>::find(tmp_);
}
typename std::unordered_map<std::string,Value>::iterator find(const std::string& v )
{
return std::unordered_map<std::string, Value>::find(v);
}
typename std::unordered_map<std::string,Value>::iterator find(const char* v )
{
tmp_.assign(v);
return std::unordered_map<std::string, Value>::find(v);
}
private:
thread_local static std::string tmp_;
};
credits:
Davide Faconti
Sorry for answering this very old question, but it still comes up in search engine results...
In this case your unordered_map is using the string type as its key, the find method is looking for a reference to a string which will not generate an allocation. Your string_view class stores a pointer to a string. Therefore your string_view class can dereference the pointer into a ref of the type needed for your map without causing an allocation. The method would look like this...
string &string_view::getRef() const
{
return *_ptr;
}
and to use the string_view with the map it would look like this
auto found=map.find(string_view_inst.getRef());
note that this will not work for the c++17 string_view class as it does not internally store a std::string object
ps.
Your string_view class is probably not great for cpu caches as it stores a pointer to a string allocated somewhere on the heap, and the string itself stores a pointer to the actual data located somewhere else on the heap. Every time you access your string_view it will result in a double dereference.
You could allow your view to be implicitly convertible to a std::string:
class StringView {
// ...
operator std::string() const
{
return data->substr(begin, len);
}
// ...
};
When using a std::pair or std::map, we need to use "first" or "second" to access data. But the two variable name do not have clear meanings of what it really store for other co-workers that did not write this code. So if we can make aliases for "first" or "second", it would enhance much readability.
For example, the following code
static const std::map<std::string, std::pair<std::string, PFConvert>> COMM_MAP =
{ // keyword-> (caption, function)
{std::string("1"), {std::string("Big5 to Utf16LE"), &FileConvert_Big5ToUtf16LE}},
{std::string("2"), {std::string("Utf16LE to Utf8"), &FileConvert_Utf16LEToUtf8}},
{std::string("3"), {std::string("Utf8 to Big5"), &FileConvert_Utf8ToBig5}}
};
auto iterToExe = COMM_MAP.find(strTransType);
iterToExe->second.second();
The iterToExe->second.second(); has a truly bad readability.
So I try to use inherit to give aliases as following
template<typename PFComm>
class CCommContent : public std::pair<std::string, PFComm>
{
public:
std::string &strCaption = std::pair<std::string, PFComm>::first;
PFComm &pfComm = std::pair<std::string, PFComm>::second;
};
template<typename PFComm>
class CCommPair : public std::pair<std::string, CCommContent<PFComm>>
{
public:
std::string &strPattern = std::pair<std::string, CCommContent<PFComm>>::first;
CCommContent<PFComm> commContent = std::pair<std::string,CCommContent<PFComm>>::second;
};
template<typename PFComm>
class CCommMap : public std::map<std::string, CCommContent<PFComm>, std::less<std::string>, std::allocator<CCommPair<PFComm>>>
{};
But this comes to an another issue: I have to declare all the ctors, though i could call the base ctors, but it still not seems to be a smart method. I Just want to make aliases.
A simple way is to use macro ...... but it bypass the type checking. when using a nested structure, it may be a nightmare when debug.
Any advice or discussion would be appreciated.
Why not simply use your own struct with your own element names?
struct MyPair {
std::string strCaption;
PFComm pfComm;
};
With C++11 you can easily create new objects of it:
MyPair{std::string("Big5 to Utf16LE"), &FileConvert_Big5ToUtf16LE}}
And if you define your own operator<, you can have std::set work as a map:
bool operator<(const MyPair& a, const MyPair& b) {
return a.strCaption < b.strCaption;
}
typedef std::set<MyPair> MyPairMap;
Naturally, you can nest your custom structs to form more complex nested pairs, although in your case you might want to consider a flat triplet instead:
struct CommMapEntry {
std::string number;
std::string caption;
PFComm pfComm;
};
bool operator<(const MyPair& a, const MyPair& b) {
return a.number<b.number;
}
static const std::set<CommMapEntry> COMM_MAP;
How about some typedefs and accessor functions?
using CommEntry = std::pair<std::string, PFConvert>;
std::string const & getCaption(CommEntry const & e) { return e.first; }
PFConvert const & getFunction(CommEntry const & e) { return e.second; }
Now you can say:
auto it = COMM_MAP.find(strTransType);
if (it != COMM_MAP.end())
{
auto & c = getCaption(it->second);
auto & l = getLabel(it->second);
// ...
}
If you later change the details of the type, you just have adapt the accessor functions.
well, in c++11, we can using base::base in a derive class to use the base ctors. But note that vs2013 DO NOT compliant this. g++4.8 do.