std::unordered_map Error in declaration - c++

In Execution.cpp, I need to add unordered_map. I used the following instruction:
#include <unordered_map>
std::unordered_map<StringRef, std::unordered_map<StringRef, struct IntRel>> BinaryRel;
but it invokes the following errors:
/usr/include/c++/4.8/bits/functional_hash.h:58:12: error: declaration of ‘struct std::hash<llvm::StringRef>’
struct hash;
/usr/include/c++/4.8/bits/hashtable_policy.h:1082:53: error: invalid use of incomplete type ‘struct std::hash<llvm::StringRef>’
using __ebo_h1 = _Hashtable_ebo_helper<1, _H1>;

You have to specialize std::hash for your StringRef type. For example:
#include <unordered_map>
struct StringRef {};
struct IntRel {};
namespace std {
template<>
struct hash<StringRef>
{
std::size_t operator()(const StringRef& s) const noexcept { return 0; }
};
}
int main()
{
std::unordered_map<StringRef, std::unordered_map<StringRef, struct IntRel>> BinaryRel;
}
Although I suggest better implementation of hashing function :D For this you can use one of existing specializations (std::hash<std::string> ? if your StringRef possibly contains std::string object).

Related

Creating a vector of the type of std::any

Consider the following example
#include <iostream>
#include <any>
#include <vector>
#include <map>
#include <typeinfo>
typedef enum TYPE{
INT8=0,
INT16=1,
INT32=2
} TYPE;
int main()
{
std::map<TYPE, std::any> myMap;
myMap[TYPE::INT8] = (int8_t)0;
myMap[TYPE::INT16] = (int16_t)0;
myMap[TYPE::INT32] = (int32_t)0;
std::vector<decltype(myMap[TYPE::INT8])> vec;
}
I have a map in this example, going from some enum to std::any. I actually need a flexible data structure that can map from a specific type (enum TYPE in this case), to multiple data types (different types of int), hence the use of std::any.
Going ahead, I would like to ascertain the type of value given for the key and construct a vector with it. I tried the above code, and it runs into a compilation error because decltype will return std::any(correctly so).
I would want to extract the "true type" from the std::any and create that type of vector. How would I achieve that.
A small snippet of the compilation error is as follows -
/opt/compiler-explorer/gcc-12.2.0/include/c++/12.2.0/bits/new_allocator.h:63:26: error: forming pointer to reference type 'std::any&'
63 | typedef _Tp* pointer;
/opt/compiler-explorer/gcc-12.2.0/include/c++/12.2.0/bits/new_allocator.h:112:7: error: forming pointer to reference type 'std::any&'
112 | allocate(size_type __n, const void* = static_cast<const void*>(0))
/opt/compiler-explorer/gcc-12.2.0/include/c++/12.2.0/bits/stl_vector.h:1293:7: error: 'void std::vector<_Tp, _Alloc>::push_back(value_type&&) [with _Tp = std::any&; _Alloc = std::allocator<std::any&>; value_type = std::any&]' cannot be overloaded with 'void std::vector<_Tp, _Alloc>::push_back(const value_type&) [with _Tp = std::any&; _Alloc = std::allocator<std::any&>; value_type = std::any&]'
1293 | push_back(value_type&& __x)
TIA
As suggested in the comments by #Ted Lyngmo, I think std::variant serves you better. Especially with C++-20's templated lambdas, the std::visit function can work wonders with these to get around the awkwardness of dealing with type enums and the like.
Note that you can not get around the runtime type detection. In any case, here is an example of how it can work.
#include <cstdint>
#include <iostream>
#include <variant>
#include <vector>
using VariantScalar = std::variant<
std::int8_t, std::int16_t, std::int32_t>;
using VariantVector = std::variant<
std::vector<std::int8_t>,
std::vector<std::int16_t>,
std::vector<std::int32_t>>;
VariantVector fill_vector(VariantScalar scalar, std::size_t n)
{
auto make_vector = [n]<class IntType>(IntType v) -> VariantVector {
return std::vector<IntType>(n, v);
};
return std::visit(make_vector, scalar);
}
void print_vector(const VariantVector& vec)
{
std::visit([]<class T>(const std::vector<T>& vec) {
for(const T& s: vec)
std::cout << s << ' ';
std::cout << '\n';
}, vec);
}
int main()
{
VariantScalar s(std::int8_t(1));
VariantVector vec = fill_vector(s, 5);
print_vector(vec);
}
Assuming you have the following enum definition:
enum class TYPE{
INT8=0,
INT16=1,
INT32=2
};
Then you can define a helper:
template <TYPE>
struct my_type {}; // Base case
template <>
struct my_type<TYPE::INT8> {
using type = int8_t;
};
template <>
struct my_type<TYPE::INT16> {
using type = int16_t;
};
template <>
struct my_type<TYPE::INT32> {
using type = int32_t;
};
template <TYPE t>
using my_type = typename my_type<t>::type;
That you can use for your vector
std::vector<my_type<TYPE::INT8>> vec;

How to declare a self-referential template typedef

Here's a small example which is substantially similar to what I'm trying to do:
#include <boost/variant/variant.hpp>
#include <boost/variant/recursive_wrapper.hpp>
#include <utility>
#include <vector>
struct foo {
const char * str;
};
typedef std::pair<float, float> fpair;
//typedef std::vector<boost::variant<int, fpair, foo, vlist>> vlist;
// ^ No...
//typedef std::vector<boost::variant<int, fpair, foo, boost::recursive_wrapper<vlist>>> vlist;
// ^ No...
//template <typename T = vlist<T> >
//using vlist = std::vector<boost::variant<int, fpair, foo, boost::recursive_wrapper<vlist>>>;
// ^ No...
template <typename T = vlist<T> >
using vlist = std::vector<boost::variant<int, fpair, foo, boost::recursive_wrapper<T>>>;
// Still no?
int main () {
std::cout << "Hello world\n";
}
The error I get with gcc 4.8 is:
test.cpp:12:33: error: expected nested-name-specifier before ‘vlist’
template <typename T = typename vlist<T>>
^
test.cpp:12:33: error: expected ‘>’ before ‘vlist’
The error with clang 3.6 is:
test.cpp:12:24: error: unknown type name 'vlist'
template <typename T = vlist<T>>
^
test.cpp:12:29: error: expected ',' or '>' in template-parameter-list
template <typename T = vlist<T>>
^
test.cpp:12:32: error: expected unqualified-id
template <typename T = vlist<T>>
^
3 errors generated.
(Edit: actually these errors are from slightly different versions of the above code, but they all give quite similar messages)
I looked at these earlier, slightly different questions, I'm still stumped:
How to declare a self referencing template type
How to properly declare a self-referencing template type?
Boost Fusion adapt declaration for a templated self referential structure
Does anyone know a trick for this, or is there some reason I'm not aware of that the compiler inherently isn't able to do this?
I believe you just want boost::make_recursive_variant:
#include <boost/variant/variant.hpp>
#include <boost/variant/recursive_variant.hpp>
#include <utility>
#include <vector>
struct foo {
const char* str;
};
typedef std::pair<float, float> fpair;
typedef boost::make_recursive_variant<
int,
fpair,
foo,
std::vector<boost::recursive_variant_>
>::type vlist;
int main() {
std::vector<vlist> vec;
vec.push_back(4);
vec.push_back(fpair{1.0f, 2.0f});
vlist v2(vec);
}

unordered key_type for non library types requires hash<> specialization?

I'm trying to create an std::unordered_map with xercesc::XMLUri as the key type.
#include <unordered_map>
#include "xercesc/util/XMLUri.hpp"
int main()
{
std::unordered_map<xercesc::XMLUri,xercesc::XMLUri> uriMap;
}
resulting in the following :
clang++ -std=c++11 -O0 -emit-llvm -g3 -Wall -c -fmessage-length=0 -I/usr/include ../xx.cpp
In file included from ../xx.cpp:1:
In file included from /usr/bin/../lib/gcc/i686-linux-gnu/4.7/../../../../include/c++/4.7/unordered_map:43:
/usr/bin/../lib/gcc/i686-linux-gnu/4.7/../../../../include/c++/4.7/bits/functional_hash.h:59:7: error: static_assert failed "std::hash is not specialized for this type"
static_assert(sizeof(_Tp) < 0,
^ ~~~~~~~~~~~~~~~
/usr/bin/../lib/gcc/i686-linux-gnu/4.7/../../../../include/c++/4.7/bits/unordered_map.h:45:32: note: in instantiation of template class 'std::hash<xercesc_3_1::XMLUri>' requested here
integral_constant<bool, !__is_final(_Hash)>,
^
/usr/bin/../lib/gcc/i686-linux-gnu/4.7/../../../../include/c++/4.7/bits/unordered_map.h:263:14: note: in instantiation of default argument for '__unordered_map<xercesc_3_1::XMLUri, xercesc_3_1::XMLUri, std::hash<xercesc_3_1::XMLUri>, std::equal_to<xercesc_3_1::XMLUri>, std::allocator<std::pair<const xercesc_3_1::XMLUri, xercesc_3_1::XMLUri> > >' required here
: public __unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
../xx.cpp:6:54: note: in instantiation of template class 'std::unordered_map<xercesc_3_1::XMLUri, xercesc_3_1::XMLUri, std::hash<xercesc_3_1::XMLUri>, std::equal_to<xercesc_3_1::XMLUri>, std::allocator<std::pair<const xercesc_3_1::XMLUri, xercesc_3_1::XMLUri> > >' requested here
std::unordered_map<xercesc::XMLUri,xercesc::XMLUri> uriMap;
I know the unordered containers in C++0x only supply hash<> specializations for some of the library types. How do I create the required hash<xercesc::XMLUri> specialization for xercesc::XMLUri?
EDIT: I came up with this. Does it seem reasonable?
#include "xercesc\util\XMLUri.hpp"
#include <string>
namespace std
{
size_t hash<xercesc::XMLUri>::operator()(const xercesc::XMLUri& uri) const
{
return hash<std::wstring>()(uri.getUriText());
}
}
Almost. It should be like this (thanks to #jogojapan for pointing out the missing typedefs!):
#include <string>
#include <functional>
namespace std
{
template <> struct hash<xercesc::XMLUri>
{
typedef size_t result_type;
typedef xercesc::XMLUri argument_type;
size_t operator()(xercesc::XMLUri const & uri) const noexcept
{
return hash<wstring>()(uri.getUriText());
}
};
}
std::hash is a struct, you must specialize the whole struct, not just the function, then your way of specializing a template is wrong too:
namespace std
{
template <>
struct hash<xercesc::XMLUri>
{
size_t operator()(const xercesc::XMLUri& uri) const
{
return hash<std::wstring>()(uri.getUriText());
}
};
}

error: no type named ‘value_type’ in ‘class

I am getting the following compiler error:
(graph_algorithms.h:59:111: error: no type named ‘value_type’ in ‘class Graph::graph_algorithms<int>::vertex_comparer’)
What does this mean?
The following line gives me a whole slew of compiler errors
std::priority_queue<typename Graph::graph<U>::vertex, typename Graph::graph_algorithms<U>::vertex_comparer> pri_que;
#ifndef GRAPH_ALGORIUHMS_H_
#define GRAPH_ALGORIUHMS_H_
#include <queue>
#include "graph.h"
namespace Graph
{
template <class U>
class graph_algorithms
{
// Forward declarations of
// internal classes
private :
class vertex_comparer;
public :
graph_algorithms(graph<U> &graph) :
m_Graph(graph)
{}
// List of algorithms to perform on graph
typename std::queue<U> &find_key_breadth_first(U key);
// Definition of internal classes
private :
class vertex_comparer
{
public:
bool operator()(typename graph<U>::vertex &v1, typename graph<U>::vertex &v2)
{
return (v1.key() < v2.key()) ? true : false;
}
};
private :
// Private member variables and methods
graph<U> &m_Graph;
std::queue<U> m_Queue;
void breadth_first_search(U key);
};
}
The comparison class should be the 3rd parameter of the std::priority_queue template, the second being a container (like std::vector<typename Graph::graph<U>::vertex>) which will have a value_type member.

Using a templated parameter's value_type

How is one supposed to use a std container's value_type?
I tried to use it like so:
#include <vector>
using namespace std;
template <typename T>
class TSContainer {
private:
T container;
public:
void push(T::value_type& item)
{
container.push_back(item);
}
T::value_type pop()
{
T::value_type item = container.pop_front();
return item;
}
};
int main()
{
int i = 1;
TSContainer<vector<int> > tsc;
tsc.push(i);
int v = tsc.pop();
}
But this results in:
prog.cpp:10: error: ‘T::value_type’ is not a type
prog.cpp:14: error: type ‘T’ is not derived from type ‘TSContainer<T>’
prog.cpp:14: error: expected ‘;’ before ‘pop’
prog.cpp:19: error: expected `;' before ‘}’ token
prog.cpp: In function ‘int main()’:
prog.cpp:25: error: ‘class TSContainer<std::vector<int, std::allocator<int> > >’ has no member named ‘pop’
prog.cpp:25: warning: unused variable ‘v’
I thought this was what ::value_type was for?
You have to use typename:
typename T::value_type pop()
and so on.
The reason is that the compiler cannot know whether T::value_type is a type of a member variable (nobody hinders you from defining a type struct X { int value_type; }; and pass that to the template). However without that function, the code could not be parsed (because the meaning of constructs changes depending on whether some identifier designates a type or a variable, e.g.T * p may be a multiplication or a pointer declaration). Therefore the rule is that everything which might be either type or variable and is not explicitly marked as type by prefixing it with typename is considered a variable.
Use the typename keyword to indicate that it's really a type.
void push(typename T::value_type& item)
typename T::value_type pop()
Here is a full implementation of the accepted answers above, in case it helps anyone.
#include <iostream>
#include <list>
template <typename T>
class C1 {
private:
T container;
typedef typename T::value_type CT;
public:
void push(CT& item) {
container.push_back(item);
}
CT pop (void) {
CT item = container.front();
container.pop_front();
return item;
}
};
int main() {
int i = 1;
C1<std::list<int> > c;
c.push(i);
std::cout << c.pop() << std::endl;
}
A fairly common practice is to provide an alias representing the underlying value type for convenience.
template <typename T>
class TSContainer {
private:
T container;
public:
using value_type = typename T::value_type;
void push(value_type& item)
{
container.push_back(item);
}
value_type pop()
{
value_type item = container.pop_front();
return item;
}
};