Template specialization works with g++ but not with Visual C++ - 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()

Related

variadic templates and initializer lists type narrowing differences

So this does not make any sense.
With this class:
template< typename T, int nDimensions = 2 >
class Vec {
private:
std::array< T, nDimensions > elements_;
public:
typedef T ValueType;
Vec() : elements_() {}
template <typename... U>
Vec(U... ts) : elements_{ ts... } {}
Vec(const std::initializer_list<T>& values){
std::copy(values.begin(), values.end(), elements_.begin());
}
template <typename T2, int nDimension2>
Vec(Vec<T2, nDimension2> const& copy){
for (int i = 0; i < nDimensions; i++){
this->elements_[i] = (T)static_cast<T2>(copy[i]);
}
}
};
Why is it perfectly fine to do:
Vec<int,2> twoi = { 1, 2.1 }; //An int and a double
But not:
Vec<int,2> twoi2(1,2.1); //conversion from 'double' to 'int' requires a narrowing conversion
That just does not seem intuitive. For the Initializer list it just preforms the conversion, while on the other hand when the template is expanded, it should just hand over the double which gets converted to a int.
This is what the function should be expanded out too if I'm correct:
std::array< int, 2> elements_ = { 1.2,1};
Which compiles just fine.
Is there a way to modify this constructor to ignore the conversions?
Visual Studios 2015 Update 3 build:
1>------ Build started: Project: MathTests, Configuration: Debug Win32 ------
1> Source.cpp
1>d:\projects\stevenstuff\mathtests\source.cpp(12): warning C4838: conversion from 'double' to 'int' requires a narrowing conversion
1>d:\projects\stevenstuff\mathtests\source.cpp(12): warning C4244: 'initializing': conversion from 'double' to 'int', possible loss of data
1>d:\projects\stevenstuff\util\vec\include\vec.h(19): error C2397: conversion from 'const double' to 'int' requires a narrowing conversion
1> d:\projects\stevenstuff\mathtests\source.cpp(10): note: see reference to function template instantiation 'Vec<int,2>::Vec<double,int>(const double &,const int &)' being compiled
Figured it out, eventually.
Simple answer is, theres is a conversion that needs to happen. Some compilers are fine with it and just cast directly, some don't and complain that its impossible. Way around it is to slip a sneaky cast into the the constructor.
template <typename... U>
Vec(const U&... ts)
: elements_{(T)(ts)... } {
}
Then everything gets expanded as: elements_((T)ts[1]... ext.

Visitor variant with multiple types (string, bool, integral, float)

I'm trying to use a variant's visitor for multiple types and then generating a new random value. Be aware that I can't use more recent compiler than Visual Studio 2015 Update 3 and GCC 4.9 because of reasons out of my control.
Here is what I have
std::random_device randomDevice;
std::mt19937 randomEngine(randomDevice());
typedef mpark::variant< // Implementation of std::variant for C++11,14
bool,
int8_t, uint8_t,
int16_t, uint16_t,
int32_t, uint32_t,
int64_t, uint64_t,
float, double,
std::string
> VariantValue;
struct ValueVisitor
{
ValueVisitor(VariantValue* pNewVal)
: pNewVal(pNewVal) {}
void operator()(const std::string & s) const
{
// Generate some random string into *pNewVal
}
void operator()(const bool& t) const
{
*pNewVal = !t;
}
template <typename T,
std::enable_if_t<std::is_integral<T>::value>* = nullptr,
std::enable_if_t<!std::is_same<T, bool>::value>* = nullptr>
>
void operator()(const T& t) const
{
std::uniform_int_distribution<T> dist
(
std::numeric_limits<T>::lowest(),
std::numeric_limits<T>::max()
);
*pNewVal = dist(randomEngine);
}
template <typename T, typename std::enable_if<
std::is_floating_point<T>::value>::type* = nullptr>
void operator()(const T& t) const
{
std::uniform_real_distribution<T> dist
(
std::numeric_limits<T>::lowest(),
std::numeric_limits<T>::max()
);
*pNewVal = dist(randomEngine);
}
VariantValue* pNewVal;
};
VariantValue vSource {(double)12 };
VariantValue vTarget;
ValueVisitor valueVisitor(&vTarget);
mpark::visite(valueVisitor, v);
But I'm getting the error C2338 invalid template argument for uniform_int_distribution.
Looking at output window for more details
1>c:\program files (x86)\microsoft visual studio 14.0\vc\include\random(2387): error C2338: invalid template argument for uniform_int_distribution
1> d:\project\xxx.cpp(271): note: see reference to class template instantiation 'std::uniform_int_distribution<T>' being compiled
1> with
1> [
1> T=int8_t
1> ]
1> d:\project\3rdparty\mpark\include\mpark\lib.hpp(237): note: see reference to function template instantiation 'void ValueVisitor::operator ()<T,void>(const T &) const' being compiled
1> with
1> [
1> T=int8_t
1> ]
...
So, If I well understood, it seems that when T=bool the functor's function targeted is the one for is_integral. But why ? I explicitly removed the bool type with std::enable_if_t<!std::is_same<T, bool>::value>* = nullptr.
I tried a different approaches like
template <typename T>
void operator()(const T & t)
{
if (std::is_same<T, bool>::value) {
} else if (std::is_floating_point<T>::value) {
} else if (std::is_integral<T>::value) {
} else if (std::is_same<T, std::string>::value) {
}
But without success, it's worst as now the float variation are still trying to use the uniform_int_distribution.
I'm really out of idea.
Best regards,

alias for multi parameter function template

I am trying to create a template for a multi-parameter function, and then an alias for a particular instantiation. From this really good post:
C++11: How to alias a function?
I found example code that works for a single function parameter and single template parameter:
#include <iostream>
namespace Bar
{
void test()
{
std::cout << "Test\n";
}
template<typename T>
void test2(T const& a)
{
std::cout << "Test: " << a << std::endl;
}
}
void (&alias)() = Bar::test;
void (&a2)(int const&) = Bar::test2<int>;
int main()
{
Bar::test();
alias();
a2(3);
}
When I try to expand to two function parameters as such:
void noBarTest(T const& a, T const& b)
{
std::cout << "noBarTest: " << a << std::endl;
}
void(&hh)(int const&, int const&) = noBarTest<int, int>;
I get these errors in Visual Studio:
error C2440: 'initializing' : cannot convert from 'void (__cdecl
*)(const T &,const T &)' to 'void (__cdecl &)(const int &,const int &)'
IntelliSense: a reference of type "void (&)(const int &, const int &)"
(not const-qualified) cannot be initialized with a value of type
""
I thought I followed the pattern exactly in expanding to 2 arguments.
What's the proper syntax for this?
template <typename T>
void noBarTest(T const& a, T const& b)
{
}
void(&hh)(int const&, int const&) = noBarTest<int>; // Only once
int main() {
return 0;
}
The type parameter int needs to be specified only once in noBarTest<int>.

Error while compiling in visual c++

I have compiled the below code :
typedef unsigned char uint8;
template <uint8 N> inline uint8 g(uint8 x) { return x > N ? 1 : 0; }
template <size_t stride, size_t boxsize, class T, class F>
inline void boxfilt(size_t width, size_t size, T * inout, const F & f) {
}
template <class T> inline T self(const T & x) { return x; }
template <size_t stride, size_t boxsize, class T>
inline void boxfilt(size_t width, size_t size, T * inout) {
return boxfilt<stride, boxsize>(width, size, inout, self<T>);
}
int main(int argc, char* argv[])
{
uint8 *out = NULL;
boxfilt<3,4>(10,29,out,g<4>);
return 0;
}
In g++ compiler, it works fine. When I try to compile the same code in Visual Studio 2008 compiler, it shows the following error:
Error 1 error C2780: 'void boxfilt(size_t,size_t,T *)' : expects 3 arguments - 4 provided g:\testfjx\test\test.cpp
Error 2 error C2784: 'void boxfilt(size_t,size_t,T *,const F &)' : could not deduce template argument for 'overloaded function type' from 'overloaded function type' g:\testfjx\test\test.cpp
Error 3 error C2784: 'void boxfilt(size_t,size_t,T *,const F &)' : could not deduce template argument for 'T *' from 'uint8 *' g:\testfjx\test\test.cpp
How can I resolve this problem?
It is OK, in Visual C++ 2008 too.
If both VC++2008 and G++4.7.2 accept the code and VC++2005 doesn't, so maybe VC++2005 has bug, maybe it doesn't implement C++ specification completely.

Using a std::tuple as key for std::unordered_map

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".