Using a std::tuple as key for std::unordered_map - c++

With the code below, I get a very confusing error in MSVC that seems to suggest the key type (an std::tuple) is being converted to an std::string.
#include <iostream>
#include <string>
#include <tuple>
#include <utility>
#include <unordered_map>
typedef std::tuple<std::string,int,char> key_t;
struct key_hash : public std::unary_function<key_t, std::size_t>
{
std::size_t operator()(const key_t& k) const
{
return std::get<0>(k)[0] ^ std::get<1>(k) ^ std::get<2>(k);
}
};
struct key_equal : public std::binary_function<key_t, key_t, bool>
{
bool operator()(const key_t& v0, const key_t& v1) const
{
return (
std::get<0>(v0) == std::get<0>(v1) &&
std::get<1>(v0) == std::get<1>(v1) &&
std::get<2>(v0) == std::get<2>(v1)
);
}
};
struct data
{
std::string x;
};
typedef std::unordered_map<key_t,data,key_hash,key_equal> map_t;
int main()
{
map_t m;
data d;
d.x = "test data";
m[std::make_tuple("abc",1,'X')] = d;
auto itr = m.find(std::make_tuple(std::string("abc"),1,'X'));
if (m.end() != itr)
{
std::cout << "x: " << itr->second.x;
}
return 0;
}
Error:
Error 1 error C2664: 'std::basic_string<_Elem,_Traits,_Ax>::basic_string(const std::basic_string<_Elem,_Traits,_Ax> &)' : cannot convert parameter 1 from 'const std::tr1::tuple<_Arg0,_Arg1,_Arg2>' to 'const std::basic_string<_Elem,_Traits,_Ax> &' c:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\include\tuple 127 1
Compiler: MS Visual Studio 2010
On ideone, I get the following even more convoluted error:
http://ideone.com/yEv2j
I can't seem to figure out where I've gone wrong.

The problem for ideone is that key_t already exists:
prog.cpp:7:42: error: conflicting declaration 'typedef class std::tuple<std::basic_string<char>, int, char> key_t'
/usr/include/sys/types.h:123:17: error: 'key_t' has a previous declaration as 'typedef __key_t key_t'
Rename your key_t to something else, or put it into some namespaces.
Your code works after this change in both g++ and clang++. I believe this is a bug in MSVC.

Strange. Your code works fine in Visual Studio 2012 RC and output is "x: test data".

Related

Cannot compile variant visitor access on MSVC 19.28

I try to compile a personal project on Visual Studio 2019 (using MSVC 19.28 compiler) and I came accross a compilation error in the std::visit which I don't understand:
<source>(131): error C2653: '`global namespace'': is not a class or namespace name
C:/data/msvc/14.28.29914/include\type_traits(1493): note: see reference to function template instantiation 'auto CommandLineOptionsParser<CmdLineOpts>::register_callback::<lambda_1>::()::<lambda_1>::operator ()<const _First&>(_T1) const' being compiled
with
[
_First=bool CmdLineOpts::* ,
_T1=bool CmdLineOpts::* const &
]
C:/data/msvc/14.28.29914/include\variant(1654): note: see reference to alias template instantiation 'std::_Variant_visit_result_t<CommandLineOptionsParser<CmdLineOpts>::register_callback::<lambda_1>::()::<lambda_1>,const std::variant<bool CmdLineOpts::* >&>' being compiled
<source>(120): note: while compiling class template member function 'void CommandLineOptionsParser<CmdLineOpts>::register_callback(const CommandLineOption &,std::variant<bool CmdLineOpts::* >)'
<source>(83): note: see reference to function template instantiation 'void CommandLineOptionsParser<CmdLineOpts>::register_callback(const CommandLineOption &,std::variant<bool CmdLineOpts::* >)' being compiled
<source>(142): note: see reference to class template instantiation 'CommandLineOptionsParser<CmdLineOpts>' being compiled
<source>(123): error C2672: 'visit': no matching overloaded function found
<source>(131): error C2893: Failed to specialize function template 'unknown-type std::visit(_Callable &&,_Variants &&...)'
C:/data/msvc/14.28.29914/include\variant(1654): note: see declaration of 'std::visit'
<source>(131): note: With the following template arguments:
<source>(131): note: '_Callable=CommandLineOptionsParser<CmdLineOpts>::register_callback::<lambda_1>::()::<lambda_1>'
<source>(131): note: '_Variants={const std::variant<bool CmdLineOpts::* > &}'
<source>(131): note: '<unnamed-symbol>=void'
Compiler returned: 2
This code compiles fine with gcc.
I tested the code snippet from cppreference on std::visit and it compiles with MSVC, so I am not so sure what the issue here.
I simplified the code and reproduced the issue on godbolt
Here's the code
#include <algorithm>
#include <functional>
#include <iostream>
#include <map>
#include <memory>
#include <sstream>
#include <string>
#include <set>
#include <string_view>
#include <variant>
#include <type_traits>
using InvalidArgumentException = std::invalid_argument;
using CommandLineOption = std::string;
template <class Opts>
class CommandLineOptionsParser : Opts {
public:
using OptionType = std::variant<bool Opts::*>;
using CommandLineOptionWithValue = std::pair<CommandLineOption, OptionType>;
Opts parse(const char* argStr) {
// First register the callbacks
bool Opts::* pBool = &Opts::help;
register_callback("help", pBool);
for (auto& cbk : _callbacks) {
cbk.second(0, argStr);
}
return static_cast<Opts>(*this);
}
private:
using callback_t = std::function<void(int, const char *)>;
std::map<CommandLineOption, callback_t> _callbacks;
void register_callback(const CommandLineOption& commandLineOption, OptionType prop) {
_callbacks[commandLineOption] = [this, &commandLineOption, prop](int idx, const char * argv) {
if (std::string(argv) == commandLineOption) {
std::visit([](auto&& a) {
using T = std::decay_t<decltype(a)>;
if constexpr (std::is_same_v<T, bool Opts::*>) {
std::cout << "bool" << std::endl;
}
},
prop);
}
};
};
};
struct CmdLineOpts {
bool help{};
};
int main(int argc, const char* argv[])
{
CommandLineOptionsParser<CmdLineOpts> p;
CmdLineOpts cmdLineOptions = p.parse("opt1");
}
It seems MSVC is having difficulty synthesizing a lambda with a pointer-to-member argument in a template context.
I tried to simplify it to a MCVE, hopefully it captures the essence of the issue:
template<class T>
bool test(int T::* t) {
return [](int T::* x) {
return true;
}(t);
}
struct A {
int a;
};
int main() {
return test<A>(&A::a);
}
It fails to compile in MSVC C++20 mode (but not C++17) with a similar nonsensical error (link):
<source>(5): error C2653: '`global namespace'': is not a class or namespace name
<source>(13): note: see reference to function template instantiation 'bool test<A>(int A::* )' being compiled
<source>(5): error C2664: 'bool test::<lambda_1>::operator ()(A *) const': cannot convert argument 1 from 'int A::* ' to 'A *'
<source>(5): note: There is no context in which this conversion is possible
<source>(5): note: see declaration of 'test::<lambda_1>::operator ()'
I would suggest to report this to the vendor.
As a potential workaround can try extracting the lambda into a functor class with a templated operator(), it seems to compile (example).

Using boost::geometry::index::indexable within an IndexableGetter in Windows

Building on top of this example:
https://www.boost.org/doc/libs/1_75_0/libs/geometry/doc/html/geometry/spatial_indexes/rtree_examples/using_indexablegetter_function_object___storing_indexes_of_external_container_s_elements.html
I have constructed the following MWE to build and query spatial trees based on indices onto non-trivial containers. The example this was based upon assumes that Container::value_type is already a leaf of the tree. I simply adapted the example to work with any indexable type, i.e., any type understood by bgi::indexable<typename Container::value_type>.
The included MWE works just fine on Linux and Mac, but fails to compile on Windows, and I'm struggling in understanding what the problem may be. A working example is here https://godbolt.org/z/vTT5r5MWc, where you can see that if you switch to gcc or clang, everything works, but with MSVC19 we get the errors reported below.
Any idea on how to modify the IndexableGetter/anything else to make this work under MSVC?
#include <boost/geometry/index/rtree.hpp>
#include <boost/geometry/strategies/strategies.hpp>
#include <boost/range/irange.hpp>
#include <iostream>
namespace bg = boost::geometry;
namespace bgi = boost::geometry::index;
template <typename LeafType,
typename IndexType = bgi::linear<16>,
typename IndexableGetter = bgi::indexable<LeafType>>
using RTree = bgi::rtree<LeafType, IndexType, IndexableGetter>;
using Point = bg::model::point<double, 2, bg::cs::cartesian>;
template <typename Container>
class IndexableGetterFromIndices
{
public:
using IndexableGetter =
typename bgi::indexable<typename Container::value_type>;
using result_type = typename IndexableGetter::result_type;
using size_t = typename Container::size_type;
explicit IndexableGetterFromIndices(Container const &c)
: container(c)
{}
result_type
operator()(size_t i) const
{
return getter(container[i]);
}
private:
const Container &container;
IndexableGetter getter;
};
template <typename IndexType = boost::geometry::index::linear<16>,
typename ContainerType>
RTree<typename ContainerType::size_type,
IndexType,
IndexableGetterFromIndices<ContainerType>>
pack_rtree_of_indices(const ContainerType &container)
{
boost::integer_range<typename ContainerType::size_type> indices(
0, container.size());
return RTree<typename ContainerType::size_type,
IndexType,
IndexableGetterFromIndices<ContainerType>>(
indices.begin(),
indices.end(),
IndexType(),
IndexableGetterFromIndices<ContainerType>(container));
}
int
main()
{
std::vector<std::pair<Point, int>> points;
// create some points
for (unsigned i = 0; i < 10; ++i)
points.push_back(std::make_pair(Point(i + 0.0, i + 0.0), i * 10));
const auto tree = pack_rtree_of_indices(points);
for (const auto result :
tree | bgi::adaptors::queried(bgi::nearest(Point(3.0, 4.0), 1)))
{
std::cout << "Nearest point: " << bg::wkt<Point>(points[result].first)
<< ", index = " << points[result].second << std::endl;
}
}
The error I get on Windows is
example.cpp
C:/data/libraries/installed/x64-windows/include\boost/geometry/index/rtree.hpp(1762): error C2664: 'boost::geometry::index::detail::translator<IndexableGetter,EqualTo>::translator(const boost::geometry::index::indexable<std::pair<Point,int>> &,const EqualTo &)': cannot convert argument 1 from 'const IndGet' to 'const boost::geometry::index::indexable<std::pair<Point,int>> &'
with
[
IndexableGetter=IndexableGetterFromIndices<std::vector<std::pair<Point,int>,std::allocator<std::pair<Point,int>>>>,
EqualTo=boost::geometry::index::equal_to<std::_Default_allocator_traits<std::allocator<std::pair<Point,int>>>::size_type>
]
and
[
IndGet=IndexableGetterFromIndices<std::vector<std::pair<Point,int>,std::allocator<std::pair<Point,int>>>>
]
C:/data/libraries/installed/x64-windows/include\boost/geometry/index/rtree.hpp(1768): note: Reason: cannot convert from 'const IndGet' to 'const boost::geometry::index::indexable<std::pair<Point,int>>'
with
[
IndGet=IndexableGetterFromIndices<std::vector<std::pair<Point,int>,std::allocator<std::pair<Point,int>>>>
]
C:/data/libraries/installed/x64-windows/include\boost/geometry/index/rtree.hpp(1762): note: No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called
C:/data/libraries/installed/x64-windows/include\boost/geometry/index/detail/translator.hpp(51): note: see declaration of 'boost::geometry::index::detail::translator<IndexableGetter,EqualTo>::translator'
with
[
IndexableGetter=IndexableGetterFromIndices<std::vector<std::pair<Point,int>,std::allocator<std::pair<Point,int>>>>,
EqualTo=boost::geometry::index::equal_to<std::_Default_allocator_traits<std::allocator<std::pair<Point,int>>>::size_type>
]
....
Thanks for a very neat self-contained example.
It does compile with /std::latest:
x86 msvc v19.latest, Live On Compiler Explorer).
x64 msvc v19.latest, Live On Compiler Explorer).
In retrospect there was the clue:
cl : Command line warning D9002 : ignoring unknown option '-std=c++2a'

Compiler warning from std::chrono but is not being used

NOTE: This bug only occurs with x64 projects in both release and debug modes.
Odd warnings involving std::chrono appear on this piece of code using VC2019 at warning level 3. This is a stripped down piece of code that processes command line flags. I've removed most of the guts that aren't relevant to the problem.
#if 1 // enable bug
#include <chrono> // excluding this also eliminates chrono warnings
using CorrectedIntType=int;
#else
using CorrectedIntType=size_t;
#endif
#include <iostream>
#include <vector>
#include <string>
#include <type_traits>
using std::vector;
using std::string;
namespace {
void fixup(const std::string& argcmd, std::string& arg) { arg = argcmd; }
template<class T>
void procVal(std::vector<std::string>& arglist, CorrectedIntType idx, T& arg)
{
fixup(arglist[idx], arg);
arglist.erase(arglist.begin() + idx);
}
template<class T, class ...TA>
void procVal(std::vector<std::string>& arglist, CorrectedIntType idx, T& arg, TA&...argv)
{
procVal(arglist, idx, arg);
procVal(arglist, idx, argv...);
}
template<class T, class ...TA>
bool procFlag(const char* pc, std::vector<std::string>& arglist, T& arg1, TA&...argv)
{
std::string flag(pc);
for (size_t i = 0; i < arglist.size(); i++)
{
if (arglist[i] == flag)
{
arglist.erase(arglist.begin() + i);
procVal(arglist, i, arg1); // process one argument after flag
return true;
}
}
return false;
}
}
int main()
{
string outfile;
vector<string> test = { "test" };
procFlag("-o", test, outfile); // assigns test[0] to outfile and removes it
std::cout << outfile << '\n';
}
Warnings:
1>Source.cpp
1>C:\Users\mgray\Documents\Visual Studio 2017\Projects\CommandLineCPP\stackoverflow\Source.cpp(35,1): warning C4267: 'argument': conversion from 'size_t' to 'CorrectedIntType', possible loss of data
1>C:\Users\mgray\Documents\Visual Studio 2017\Projects\CommandLineCPP\stackoverflow\Source.cpp(54): message : see reference to function template instantiation 'bool `anonymous-namespace'::procFlag<std::string,>(const char *,std::vector<std::string,std::allocator<std::string>> &,T &)' being compiled
1> with
1> [
1> T=std::string
1> ]
1>C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Tools\MSVC\14.24.28314\include\chrono(632): message : see reference to class template instantiation 'std::chrono::duration<double,std::ratio<1,1>>' being compiled
1>C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Tools\MSVC\14.24.28314\include\chrono(178): message : see reference to class template instantiation 'std::chrono::duration<__int64,std::nano>' being compiled
1>C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Tools\MSVC\14.24.28314\include\chrono(610): message : see reference to class template instantiation 'std::chrono::time_point<std::chrono::steady_clock,std::chrono::nanoseconds>' being compiled
While the code works, even with the int -<> size_t conversion issue which is a legitimate warning, all warnings go away when the macro at the top is set to 0. So somehow the size difference between size_t and int triggers chrono messages. It concerns me that the chrono warnings exist since it isn't involved. Is this a bug in VS2019? Any ideas as to why the chrono warning references are occurring?
This is a valid warning and it is not related to <chrono> but to your own code and CorrectedIntType type. Here is a simplified code without <chrono>: https://gcc.godbolt.org/z/qf9v8TEh7
In the definition of procVal, the second parameter is CorrectedIntType:
void procVal(std::vector<std::string>& arglist, CorrectedIntType idx, T& arg)
but it is called from procFlag with size_t value:
bool procFlag(const char* pc, std::vector<std::string>& arglist, T& arg1)
...
for (size_t i = 0; i < arglist.size(); i++)
...
procVal(arglist, i, arg1);
So one can fix the warning by changing i type to CorrectedIntType as well.

Why does the order of template parameters matter to the MS C++ compiler in this example?

The following code compiles fine in GCC but in Visual Studio it results in
error C2782: 'bool contains(const T &,const std::initializer_list<T2>
&)' : template parameter 'T' is ambiguous see declaration of
'contains' could be 'const wchar_t *' or 'std::wstring'
It does however compile and work if the order of the template parameters is given as
template<typename T2, typename T>
Is this a compiler bug?
#include <string>
#include <iostream>
#include <set>
#include <initializer_list>
#include <algorithm>
template<typename T, typename T2>
bool contains(T const& value, std::initializer_list<T2> const& set)
{
return std::find(std::begin(set), std::end(set), value) != std::end(set);
}
int main(void)
{
std::set<std::wstring> values = { L"bar", L"not" };
for (std::wstring val : values) {
std::wcout << "\"" << val << "\" ";
if (contains(val, { L"foo", L"bar", L"baz", L"doom" })) {
std::wcout << "found" << std::endl;
}
else {
std::wcout << "not found" << std::endl;
}
}
}
Edit: I have created a bugreport: https://connect.microsoft.com/VisualStudio/feedbackdetail/view/982338/template-parameter-order-matters
I remember that VS has a bug where they would do double-deduction in certain scenarios, and I think that's what's happening here. Clang also compiles it both ways, so since clang + gcc agree, it's likely a VS bug.
I had a similar problem which was resolved by switching to the latest VS Pro version. I think this bug was addressed in the latest VS pro version as I remember seeing it in a change-log at some point.

Template specialization works with g++ but not with Visual C++

I have a bunch of templated code that compiles fine under g++, but now when I try to build under windows with Visual C++ 2010 I get a bunch of errors.
I have a collection of template functions for getting and setting values in C++ objects from Lua code. For example, I have this template:
// Class Return type Getter function
template <typename T, typename U, U (T::*Getter)() const>
int luaU_get(lua_State* L)
{
T* obj = luaW_check<T>(L, 1); // Gets userdata from stack and checks if it's of type T
luaU_push(L, (obj->*Getter)()); // Runs the getter function specified in the template, and pushes the
return 1;
}
(The complete file can be found here)
Which is instantiated here:
static luaL_reg TextArea_MT[] =
{
// Class Return type Getter function
{ "GetCharacterSize", luaU_get<TextArea, unsigned int, &TextArea::GetCharacterSize> },
{ NULL, NULL }
};
The signature for that getter is as follows:
unsigned int GetCharacterSize() const;
I'm getting a bunch of errors like this:
2>C:\Users\Alex\Documents\Visual Studio 2010\Projects\game\dev\src\game\lua\LuaTextArea.cpp(103): error C2440: 'specialization' : cannot convert from 'unsigned int (__thiscall ag::ui::TextArea::* )(void) const' to 'unsigned int *(__thiscall ag::ui::TextArea::* const )(void) const'
2> Types pointed to are unrelated; conversion requires reinterpret_cast, C-style cast or function-style cast
2>C:\Users\Alex\Documents\Visual Studio 2010\Projects\game\dev\src\game\lua\LuaTextArea.cpp(103): error C2973: 'luaU_get' : invalid template argument 'unsigned int (__thiscall ag::ui::TextArea::* )(void) const'
2> C:\Users\Alex\Documents\Visual Studio 2010\Projects\game\dev\src\extern\LuaWrapper\LuaWrapperUtil.hpp(147) : see declaration of 'luaU_get'
2>C:\Users\Alex\Documents\Visual Studio 2010\Projects\game\dev\src\game\lua\LuaTextArea.cpp(103): error C2440: 'specialization' : cannot convert from 'unsigned int (__thiscall ag::ui::TextArea::* )(void) const' to 'unsigned int *ag::ui::TextArea::* const '
2> There is no context in which this conversion is possible
2>C:\Users\Alex\Documents\Visual Studio 2010\Projects\game\dev\src\game\lua\LuaTextArea.cpp(103): error C2973: 'luaU_get' : invalid template argument 'unsigned int (__thiscall ag::ui::TextArea::* )(void) const'
2> C:\Users\Alex\Documents\Visual Studio 2010\Projects\game\dev\src\extern\LuaWrapper\LuaWrapperUtil.hpp(131) : see declaration of 'luaU_get'
2>C:\Users\Alex\Documents\Visual Studio 2010\Projects\game\dev\src\game\lua\LuaTextArea.cpp(103): error C2440: 'specialization' : cannot convert from 'unsigned int (__thiscall ag::ui::TextArea::* )(void) const' to 'unsigned int ag::ui::TextArea::* const '
2> There is no context in which this conversion is possible
2>C:\Users\Alex\Documents\Visual Studio 2010\Projects\game\dev\src\game\lua\LuaTextArea.cpp(103): error C2973: 'luaU_get' : invalid template argument 'unsigned int (__thiscall ag::ui::TextArea::* )(void) const'
2> C:\Users\Alex\Documents\Visual Studio 2010\Projects\game\dev\src\extern\LuaWrapper\LuaWrapperUtil.hpp(123) : see declaration of 'luaU_get'
2>C:\Users\Alex\Documents\Visual Studio 2010\Projects\game\dev\src\game\lua\LuaTextArea.cpp(103): error C2440: 'initializing' : cannot convert from 'overloaded-function' to 'lua_CFunction'
2> None of the functions with this name in scope match the target type
This is a compiler bug in VC++. The following code is valid:
#include <iostream>
struct TextArea
{
unsigned GetCharacterSize() const { return 0; }
};
template<typename T, typename U, U (T::*)() const>
int foo()
{
return 1;
}
template<typename T, typename U, U* (T::*)() const>
int foo()
{
return 2;
}
int main()
{
std::cout << foo<TextArea, unsigned, &TextArea::GetCharacterSize>() << '\n';
}
And compiles with GCC 4.3.4, GCC 4.5.1, and Comeau 4.3.10.1 Beta2 (no link), but yields the following error with VC++ 2010 SP1:
error C2668: 'foo' : ambiguous call to overloaded function
EDIT: As for a workaround, it's ugly, but the only thing I can think of offhand is to use an extra layer of indirection so that there is no overloading involved:
#include <iostream>
struct WithPointer
{
unsigned* GetCharacterSize() const { return nullptr; }
};
struct WithoutPointer
{
unsigned GetCharacterSize() const { return 0u; }
};
template<bool UsePointerImplB>
struct kludge
{
template<typename T, typename U, U (T::*Getter)() const>
static int foo() { return 1; }
};
template<>
struct kludge<true>
{
template<typename T, typename U, U* (T::*Getter)() const>
static int foo() { return 2; }
};
int main()
{
std::cout
<< kludge<false>::foo<WithoutPointer, unsigned, &WithoutPointer::GetCharacterSize>() << '\n'
<< kludge<true>::foo<WithPointer, unsigned, &WithPointer::GetCharacterSize>() << '\n';
}
Effectively this is no different than just giving each overload a different name...
If you can force user to pick the actual return type of the function, the following works. Maybe, it'll be useful to you:
#include <iostream>
struct FooBar
{
int Foo( void ) const
{
std::cout << "FooBar::Foo()" << std::endl;
return ( 0 );
}
int * Bar( void ) const
{
std::cout << "FooBar::Bar()" << std::endl;
return ( 0 );
}
};
template< typename P00, typename P01, P01(P00::*p02)( void ) const >
void Call()
{
P00 lT;
( lT.*p02 )();
}
int main( void )
{
Call< FooBar, int, &FooBar::Foo > ();
Call< FooBar, int*, &FooBar::Bar > ();
return( 0 );
}
Program output:
FooBar::Foo()
FooBar::Bar()