Adding a struct into a map - c++

I'm having trouble adding a struct into a map. I don't really understand the error.
There are 2 errors:
I can't declare a map with 'struct' type
I can't use insert to 'insert' my struct into the map
What am I doing wrong?
#include <iostream>
#include <map>
int main()
{
typedef struct
{
std::string stringVar;
unsigned unsignedVar;
float floatVar;
} MyTypeDefStruct;
MyTypeDefStruct myTypeDefStruct;
myTypeDefStruct.stringVar = "myStr";
myTypeDefStruct.unsignedVar = 1000;
myTypeDefStruct.floatVar = -10.0;
float anotherFloat = -20.0;
std::map<MyTypeDefStruct, float> myMap;
myMap.insert(std::pair<MyTypeDefStruct, float> (myTypeDefStruct, anotherFloat));
return 0;
}
error:
test.cpp: In function 'int main()':
test.cpp:21:36: error: template argument for 'template<class _Tp> struct std::less' uses local type 'main()::MyTypeDefStruct'
std::map<MyTypeDefStruct, float> myMap;
^
test.cpp:21:36: error: trying to instantiate 'template<class _Tp> struct std::less'
test.cpp:21:36: error: template argument 3 is invalid
test.cpp:21:36: error: template argument for 'template<class _T1, class _T2> struct std::pair' uses local type 'const main()::MyTypeDefStruct'
test.cpp:21:36: error: trying to instantiate 'template<class _T1, class _T2> struct std::pair'
test.cpp:21:36: error: template argument 4 is invalid
test.cpp:21:43: error: invalid type in declaration before ';' token
std::map<MyTypeDefStruct, float> myMap;
^
test.cpp:23:11: error: request for member 'insert' in 'myMap', which is of non-class type 'int'
myMap.insert(std::pair<MyTypeDefStruct, float> (myTypeDefStruct, anotherFloat));
^
test.cpp:23:50: error: template argument for 'template<class _T1, class _T2> struct std::pair' uses local type 'main()::MyTypeDefStruct'
myMap.insert(std::pair<MyTypeDefStruct, float> (myTypeDefStruct, anotherFloat));
^
test.cpp:23:50: error: trying to instantiate 'template<class _T1, class _T2> struct std::pair'

There are several issues in your code:
The struct should defined outside of the function main, can't see a reason why you want to define it there in this case, and since it is local inside of main, you can't use it anywhere else!
You are using the C-style typedef struct ... StructName instead of simply using struct MyStruct.
Your struct doesn't implement the operator<, hence, since std::map is an ordered map, and it doesn't have anyway to compare two keys (here, the key is your struct), it doesn't able to insert any pair.
Your struct doesn't implement the operator== which is important to actually retrieving the value of a certain key... You should be able to check if the keys are similar.
Another small fix - using std::make_pair instead of its ctor.
Your code should look like:
#include <iostream>
#include <map>
struct MyStruct
{
std::string stringVar;
unsigned unsignedVar;
float floatVar;
friend bool operator<(const MyStruct& l, const MyStruct& r)
{
return std::tie(l.stringVar, l.unsignedVar, l.floatVar)
< std::tie(r.stringVar, r.unsignedVar, r.floatVar);
}
friend bool operator==(const MyStruct& l, const MyStruct& r)
{
return std::tie(l.stringVar, l.unsignedVar, l.floatVar)
== std::tie(r.stringVar, r.unsignedVar, r.floatVar);
}
};
int main()
{
MyStruct my_struct;
my_struct.stringVar = "myStr";
my_struct.unsignedVar = 1000;
my_struct.floatVar = -10.0;
float anotherFloat = -20.0;
std::map<MyStruct, float> myMap;
myMap.insert(std::make_pair(my_struct, anotherFloat));
return 0;
}
How did I get to identifying the first problem? In the first error (always look at the first error, the others may be misleading!), it is written:
test.cpp:21:36: error: template argument for 'template<class _Tp> struct std::less' uses local type 'main()::MyTypeDefStruct'
std::map<MyTypeDefStruct, float> myMap;
Notice that the template initialization here is using main()::MyTypeDefStruct and not MyTypeDefStruct!!! These types are different, and the first is available only in the scope of main()!
The third point is because when you fix the first point, you will get:
error: no match for 'operator<' (operand types are 'const MyStruct' and 'const MyStruct')

The first problem is that map requires a way to compare two myTypeDefStructs.
You'll need to do is define operator< or std::less for your class, or pass a comparison functor to the map.

std::map<T>::insert uses a comparator to insert your elements but there is no one defined for your struct, hence try to use something like the following
#include <iostream>
#include <map>
struct MyTypeDefStruct
{
std::string stringVar;
unsigned unsignedVar;
float floatVar;
} ;
bool operator<(const MyTypeDefStruct& lhs, const MyTypeDefStruct& rhs){
return lhs.unsignedVar < rhs.unsignedVar;
}
int main()
{
MyTypeDefStruct myTypeDefStruct;
myTypeDefStruct.stringVar = "myStr";
myTypeDefStruct.unsignedVar = 1000;
myTypeDefStruct.floatVar = -10.0;
float anotherFloat = -20.0;
std::map<MyTypeDefStruct, float> myMap;
myMap.insert(std::pair<MyTypeDefStruct, float> (myTypeDefStruct, anotherFloat));
return 0;
}

A sorted map needs a less operator (operator<) to sort the elements. You have to provide such operator or you have to set a sort function. One possible solution is to implement this operator:
#include <iostream>
#include <map>
struct MyTypeDefStruct {
std::string stringVar;
unsigned unsignedVar;
float floatVar;
};
bool operator<(const MyTypeDefStruct &lhs, const MyTypeDefStruct &rhs) {
if (lhs.stringVar < rhs.stringVar) return true;
if (lhs.stringVar > rhs.stringVar) return false;
if (lhs.unsignedVar < rhs.unsignedVar) return true;
if (lhs.unsignedVar > rhs.unsignedVar) return false;
if (lhs.floatVar < rhs.floatVar) return true;
return false;
}
int main() {
MyTypeDefStruct myTypeDefStruct;
myTypeDefStruct.stringVar = "myStr";
myTypeDefStruct.unsignedVar = 1000;
myTypeDefStruct.floatVar = -10.0;
float anotherFloat = -20.0;
std::map<MyTypeDefStruct, float> myMap;
myMap.insert(std::pair<MyTypeDefStruct, float> (myTypeDefStruct, anotherFloat));
return 0;
}

Related

Unordered map hash function

#include <iostream>
#include <unordered_map>
#include <utility>
#include <cmath>
#include <stdint.h>
template <class T>
struct Vec2
{
T x, y;
Vec2() : x(0) , y(0) { };
Vec2(T xn, T yn) : x(xn), y(yn) { };
bool operator==(const Vec2& vec) const
{
return (x == vec.x) and (y == vec.y);
}
};
struct HashVec2int
{
const uint32_t maximum;
// HashVec2int(const uint32_t& m) : maximum(m) { }
std::size_t operator()(const Vec2<int>& vec) const
{ return (vec.x * maximum) + vec.y; }
};
int main() {
typedef Vec2<int> iVec2;
std::unordered_map<std::pair<iVec2, iVec2>,
float, HashVec2int{300}> umap;
umap[std::make_pair(iVec2(1, 2), iVec2(2, 3))] = 3.14f;
std::cout << umap[std::make_pair(iVec2(1, 2), iVec2(2, 3))];
return 0;
}
I want to initialize a hash function with a maximum x value to hash my implementation of vector.
I changed the struct to a class a couple of times and even marked maximum manually inside of HashVec2int. It gave me cryptic errors.
.\main.cpp:35:32: error: type/value mismatch at argument 3 in template parameter list for 'template<class _Key, class _Tp, class _Hash, class _Pred, class _Alloc> class std::unordered_map'
float, HashVec2int{300}> umap;
^
.\main.cpp:35:32: note: expected a type, got 'HashVec2int{300u}'
.\main.cpp:37:6: error: no match for 'operator[]' (operand types are 'int' and 'std::pair<Vec2<int>, Vec2<int> >')
umap[std::make_pair(iVec2(1, 2), iVec2(2, 3))] = 3.14f;
^
.\main.cpp:39:19: error: no match for 'operator[]' (operand types are 'int' and 'std::pair<Vec2<int>, Vec2<int> >')
std::cout << umap[std::make_pair(iVec2(1, 2), iVec2(2, 3))];
^
I followed cppreference tutorials on how to create a modified hash function.
You have a map with keys of pairs of iVecs but your hash is for single iVecs, that doesnt match. Though this will only be the next error you will be getting after fixing the current one.
It seems you try to pass an instance HashVec2int{300} as template argument when a type is expected.
If maximum does not change (it should not for the hash to be consistent) you can do it like this:
template <uint32_t maximum>
struct HashVec2int
{
std::size_t operator()(const Vec2<int>& vec) const
{ return (vec.x * maximum) + vec.y; }
};
int main() {
typedef Vec2<int> iVec2;
std::unordered_map<iVec2,
float, HashVec2int<200>> umap;
umap[iVec2(1, 2)] = 3.14f;
std::cout << umap[iVec2(1, 2)];
return 0;
}
If you do not want to make HashVec2int a template you can still use it, then use its type Hashvec2Int as argument for the unordered map template and pass an instance, eg HashVec2Int{200}, to one of the constructors that takes a hash instance: https://en.cppreference.com/w/cpp/container/unordered_map/unordered_map

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;

Use a function pointer as template function type parameter?

#include <iostream>
#include <unordered_map>
#include <utility>
#include <typeinfo>
using namespace std;
class Handle{
public:
int val;
bool getAskPrice(int& tmp) const
{
tmp = val;
return true;
}
bool setAskPrice(int& tmp)
{
val = tmp;
return true;
}
};
template<class RT, class ARG>
struct convertToAFL{
static RT to_afl(ARG);
};
template<class RT, class ARG>
struct convertFromAFL{
static RT from_afl(ARG);
};
template<>
struct convertToAFL<float, int>
{
static float to_afl(int& value)
{
return static_cast<float>(value);
}
};
template<>
struct convertFromAFL<int, float>
{
static int from_afl(float& val)
{
return static_cast<int>(val);
}
};
struct Getter{
template<typename TICK_D, bool (Handle::*Getter)(TICK_D&) const, typename AFL_D>
static AFL_D getter(const Handle& handle)
{
TICK_D temp;
bool exists;
exists = (handle.*Getter)(temp);
AFL_D x = convertToAFL<AFL_D, TICK_D>::to_afl(temp);
return exists ? x : -1;
}
};
struct Setter{
template<typename TICK_D, bool (Handle::*Setter)(TICK_D&), typename AFL_D>
static void setter(Handle& handle, AFL_D& val)
{
TICK_D x;
x = convertFromAFL<TICK_D, AFL_D>::from_afl(val);
(handle.*Setter)(x);
}
};
int main()
{
Handle h;
float val = 20.0;
Setter::setter<int, &Handle::setAskPrice, float>(h, val);
std::cout<<Getter::getter<int, &Handle::getAskPrice, float>(h);
//std::pair<, &Setter::setter<int, &Handle::setAskPrice, float>> x;
return 0;
}
The above code works as expected, however, in the main() instead of calling the functions, how can I store the pointer to the templatized Setter:setter() and Getter::getter() ?
I m trying something like
std::pair<&Setter::setter<int, &Handle::setAskPrice, float>, &Getter::getter<int, &Handle::getAskPrice, float>(h)> func_pair;
And be able to call the functions later.
But i get an error saying
main.cpp: In function ‘int main()’:
main.cpp:85:118: error: type/value mismatch at argument 1 in template parameter list for ‘template struct std::pair’
std::pair<&Setter::setter<int, &Handle::setAskPrice, float>, &Getter::getter<int, &Handle::getAskPrice, float>(h)> func_pair;
^
main.cpp:85:118: note: expected a type, got ‘& setter’
main.cpp:85:118: error: template argument 2 is invalid
Static member functions are just a normal functions. You can store these pointers like this:
std::pair<void (*)(Handle& handle, float& val), float (*)(const Handle& handle)>
func_pair(&Setter::setter<int, &Handle::setAskPrice, float>, &Getter::getter<int, &Handle::getAskPrice, float>);
Your problem is that template parameter is a type, but you are passing a value (pointer) as an argument. Instead, you could use auto like this:
auto func_pair = std::make_pair(&Setter::setter<int, &Handle::setAskPrice, float>, &Getter::getter<int, &Handle::getAskPrice, float>);
Edit: if you are using C++03, std::make_pair() is still available, but not auto. You will need to describe the type manually with a series of typedefs.
You can use decltype to get the pointer type.
Example:
std::pair<int, decltype(&Setter::setter<int, &Handle::setAskPrice, float>)> x = {
1, &Setter::setter<int, &Handle::setAskPrice, float>
};
Prior to C++11:
std::pair<int, void(*)(Handle&, float&)> x(
1, &Setter::setter<int, &Handle::setAskPrice, float>
);

operator== does not compile if I include <iostream>

The following code compiles perfectly if:
I don't include <iostream> or
I name operator== as alp::operator==.
I suppose there is a problem with <iostream> and operator==, but I don't know what.
I compile the code with gcc 7.3.0, clang++-6.0 and goldbolt. Always the same error.
The problem is that the compiler is trying to cast the parameters of operator== to const_iterator, but why? (I suppose the compiler doesn't see my version of operator==, and looks for other versions).
#include <vector>
#include <iostream> // comment and compile
namespace alp{
template <typename It_base>
struct Iterator {
using const_iterator = Iterator<typename It_base::const_iterator>;
operator const_iterator() { return const_iterator{}; }
};
template <typename It_base>
bool operator==(const Iterator<It_base>& x, const Iterator<It_base>& y)
{ return true;}
}// namespace
struct Func{
int& operator()(int& p) const {return p;}
};
template <typename It, typename View>
struct View_iterator_base{
using return_type = decltype(View{}(*It{}));
using const_iterator =
View_iterator_base<std::vector<int>::const_iterator, Func>;
};
using view_it =
alp::Iterator<View_iterator_base<std::vector<int>::iterator, Func>>;
int main()
{
view_it p{};
view_it z{};
bool x = operator==(z, p); // only compiles if you remove <iostream>
bool y = alp::operator==(z,p); // always compile
}
Error message:
yy.cpp: In instantiation of ‘struct View_iterator_base<__gnu_cxx::__normal_iterator<const int*, std::vector<int> >, Func>’:
yy.cpp:9:73: required from ‘struct alp::Iterator<View_iterator_base<__gnu_cxx::__normal_iterator<const int*, std::vector<int> >, Func> >’
yy.cpp:44:29: required from here
yy.cpp:28:42: error: no match for call to ‘(Func) (const int&)’
using return_type = decltype(View{}(*It{}));
~~~~~~^~~~~~~
yy.cpp:22:10: note: candidate: int& Func::operator()(int&) const <near match>
int& operator()(int& p) const {return p;}
^~~~~~~~
yy.cpp:22:10: note: conversion of argument 1 would be ill-formed:
yy.cpp:28:42: error: binding reference of type ‘int&’ to ‘const int’ discards qualifiers
using return_type = decltype(View{}(*It{}));
~~~~~~^~~~~~~
I've made a more minimal test case here: https://godbolt.org/z/QQonMG .
The relevant details are:
A using type alias does not instantiate a template. So for example:
template<bool b>
struct fail_if_true {
static_assert(!b, "template parameter must be false");
};
using fail_if_used = fail_if_true<true>;
will not cause a compile time error (if fail_if_used isn't used)
ADL also inspects template parameter classes. In this case, std::vector<int>::iterator is __gnu_cxx::__normal_iterator<const int*, std::vector<int> >, Func>, which has a std::vector<int> in it's template. So, operator== will check in the global namespace (always), alp (As alp::Iterator is in alp), __gnu_cxx and std.
Your View_iterator_base::const_iterator is invalid. View_iterator_base::const_interator::result_type is defined as decltype(Func{}(*std::vector<int>::const_iterator{})). std::vector<int>::const_iterator{} will be a vectors const iterator, so *std::vector<int>::const_iterator{} is a const int&. Func::operator() takes an int&, so this means that the expression is invalid. But it won't cause a compile time error if not used, for the reasons stated above. This means that your conversion operator is to an invalid type.
Since you don't define it as explicit, the conversion operator (To an invalid type) will be used to try and match it to the function parameters if they don't already match. Obviously this will finally instantiate the invalid type, so it will throw a compile time error.
My guess is that iostream includes string, which defines std::operator== for strings.
Here's an example without the std namespace: https://godbolt.org/z/-wlAmv
// Avoid including headers for testing without std::
template<class T> struct is_const { static constexpr const bool value = false; } template<class T> struct is_const<const T> { static constexpr const bool value = true; }
namespace with_another_equals {
struct T {};
bool operator==(const T&, const T&) {
return true;
}
}
namespace ns {
template<class T>
struct wrapper {
using invalid_wrapper = wrapper<typename T::invalid>;
operator invalid_wrapper() {}
};
template<class T>
bool operator==(const wrapper<T>&, const wrapper<T>&) {
return true;
}
}
template<class T>
struct with_invalid {
static_assert(!is_const<T>::value, "Invalid if const");
using invalid = with_invalid<const T>;
};
template<class T>
void test() {
using wrapped = ns::wrapper<with_invalid<T>>;
wrapped a;
wrapped b;
bool x = operator==(a, b);
bool y = ns::operator==(a, b);
}
template void test<int*>();
// Will compile if this line is commented out
template void test<with_another_equals::T>();
Note that just declaring operator const_iterator() should instantiate the type. But it doesn't because it is within templates. My guess is that it is optimised out (where it does compile because it's unused) before it can be checked to show that it can't compile (It doesn't even warn with -Wall -pedantic that it doesn't have a return statement in my example).

Boost phoenix member function operator fails to compile if function has overload

I want to use Boost phoenix member function operator for the class function that has overloads, like here.
The following example fails:
#include <boost/phoenix/phoenix.hpp>
#include <boost/phoenix/operator.hpp>
using namespace std;
using namespace boost::phoenix::placeholders;
struct A
{
int m_id = 1;
int func() const { return 1; }
void func(int id) { m_id = id; }
};
int main()
{
A *a = new A;
auto retVal = (arg1->*&A::func)()(a);
return 0;
}
With error:
In function 'int main()': 17:21: error: no match for 'operator->*'
(operand types are 'const type {aka const
boost::phoenix::actor<boost::proto::exprns_::basic_expr<boost::proto::tagns_::
tag::terminal, boost::proto::argsns_::term<boost::phoenix::argument<1> >, 0l>
>}' and '<unresolved overloaded function type>') 17:21: note: candidate is: In
file included from /usr/include/boost/phoenix/operator/arithmetic.hpp:13:0,
from /usr/include/boost/phoenix/operator.hpp:13, from /usr/include/boost
/phoenix/phoenix.hpp:13, from 1: /usr/include/boost/proto/operators.hpp:295:9:
note: template<class Left, class Right> const typename
boost::proto::detail::enable_binary<boost::proto::domainns_::deduce_domain,
boost::proto::detail::not_a_grammar,
boost::mpl::or_<boost::proto::is_extension<Arg>,
boost::proto::is_extension<Right> >, boost::proto::tagns_::tag::mem_ptr, const
Left&, const Right&>::type boost::proto::exprns_::operator->*(Left&&,
Right&&) BOOST_PROTO_DEFINE_OPERATORS(is_extension, deduce_domain) ^
/usr/include/boost/proto/operators.hpp:295:9: note: template argument
deduction/substitution failed: 17:28: note: couldn't deduce template parameter
'Right'
However, if I comment the line void func(int id) { m_id = id; } out, it works as expected.
How can I tell which of the overloads to use?
Handling (member) function pointers to overload sets is always a pain. You need to cast the address to a pointer that has the exact signature of the desired overload. In your case, for selection int A::func():
auto retVal = (arg1->*static_cast<int (A::*)() const>(&A::func))()(a);
or a bit more readable, but basically the same:
const auto memFctPtr = static_cast<int (A::*)() const>(&A::func);
auto retVal = (arg1->*memFctPtr)()(a);