I'm trying to create a template class to insulate the users from a data type. I would have preferred to use an adapter class, but the function signatures needed to change requiring a template.
In the code sample below(not the actual project just a simplified version to illustrate the problem), while in the main routine I'm able to use the ob_traits interface. But when I attempt to create the templated StructWrapper which uses the ob_traits as a base class, I get errors and gcc doesn't recognize the IntAdapter class created. This compiles on MSVC 8.0 but fails on gcc 4.1.2 20070626 ( Red hat 4.1.2-14)
So two questions first, do you understand why the compile fails with the errors specified below?
Second, any suggestions on how to implement this concept in a more simple manner?
#include <iostream>
template <typename T >
struct ob_traits
{
ob_traits( T& param ) { value = param; };
T value;
};
struct GeneralStructure
{
int a;
GeneralStructure(int param):a(param){}
};
struct DifferentStructure
{
GeneralStructure hidden;
DifferentStructure( int param ):hidden(param){};
}
;
/*template< typename T > struct ob_traits
{
};
*/
template<> struct ob_traits< GeneralStructure >
{
struct IntAdapter
{
IntAdapter( GeneralStructure& valueParam ):value(valueParam){}
GeneralStructure value;
int& getValue() { return value.a; };
};
};
template<> struct ob_traits< DifferentStructure >
{
struct IntAdapter
{
IntAdapter( DifferentStructure& valueParam):value( valueParam ){}
DifferentStructure value;
int& getValue( ){ return value.hidden.a; };
};
void dump()
{
DifferentStructure testLocal(44);
IntAdapter local( testLocal );
std::cout << local.getValue()<<std::endl;
}
};
template <typename T > struct StructWrapper:public ob_traits< T >
{
StructWrapper(){};
/*main.cpp:60: error: 'IntAdapter' was not declared in this scope
main.cpp:60: error: expected `;' before 'inner'
main.cpp:60: error: 'inner' was not declared in this scope
*/
void dumpOuter(const T& tempParam) { IntAdapter inner(tempParam); inner.dump(); };
/*
main.cpp: In member function 'void StructWrapper<T>::dumpOuterFailsAsWell(const T&)':
main.cpp:66: error: expected `;' before 'inner'
main.cpp:66: error: 'inner' was not declared in this scope
*/
void dumpOuterFailsAsWell(const T& tempParam) { ob_traits<T>::IntAdapter inner(tempParam); inner.dump(); };
};
int main(int argc, char* argv[])
{
GeneralStructure dummyGeneral(22);
ob_traits<struct GeneralStructure >::IntAdapter test(dummyGeneral);
DifferentStructure dummyDifferent(33);
ob_traits<struct DifferentStructure >::IntAdapter test2(dummyDifferent);
std::cout << "GeneralStructure: "<<test.getValue()<<std::endl;
std::cout << "DifferentStructure: "<<test2.getValue()<<std::endl;
ob_traits<struct DifferentStructure > test3;
test3.dump();
std::cout << "Test Templated\n";
return 0;
}
dumpOuter fails because IntAdapter needs to be qualified (as in the referenced question). dumpOuterFailsAsWell fails because GCC does parsing of this code,even though it's not complete, and so it needs to know it's a type that you mean:
void dumpOuterWorks(const T& tempParam)
{
typename ob_traits<T>::IntAdapter inner(tempParam);
inner.dump();
}
Without typename here, GCC will assume that IntAdapter is an identifier, and will expect you to be forming an expression rather than a variable declaration.
Also note that you do not have to put semicolons after method bodies!
StructWrapper is inheriting from the primary class template (ie the least specialized), which does not define IntWrapper, so it cannot be used in this class. I'm not sure if instantiating a StructWrapper with one of the more specialized types will allow it to work, or if it fails on compiling the class definition itself.
Compile fails because IntAdapter only appears in the specialized template, and therefore it is not visible at the referring point.
Not clear what would you use it for? Please clarify the circumstances.
Related
I tried to use some of the new features of C++11/14 and came across a nasty thing with type deduction of class methods within their defition.
The scenario:
// in header foo.hpp
class MyClass {
T foo();
}
//in source foo.cpp
auto MyClass::foo() {
return ... //something that returns T!
}
For T = cl_uint (OpenCL) this does not work and the compiler outputs the following error message:
src/device.cpp:9:7: error: prototype for ‘auto CL::Device::addressBits() const’ does not match any in class ‘CL::Device’
and
src/device.hpp:31:11: error: candidate is: cl_uint CL::Device::addressBits() const
This behaves equally with the newest versions of GCC and Clang.
The concrete example is as follows:
// in the .hpp
namespace CL {
class Device : public Object<cl_device_id, cl_device_info, DeviceFunctions> {
public:
Device(cl_device_id id);
cl_uint addressBits() const;
// much more stuff ... (not of interest atm)
}
}
// in the .cpp
namespace CL {
auto Device::addressBits() const {
return getInfo<cl_uint>(CL_DEVICE_ADDRESS_BITS);
}
}
// in object.hpp => inherited by device
namespace CL {
template<typename U, typename InfoIdType, typename Functions>
class Object {
protected:
template<typename T>
T getInfo(InfoIdType info_id) const {
auto error = cl_int{CL_INVALID_VALUE};
auto info = T{};
error = Functions::get_info(m_id, info_id, sizeof(T), &info, nullptr);
return (error == CL_SUCCESS) ? info : T{};
}
}
}
I am well aware that this problem doesn't lead to anything terrible nor isn't it fixable by leaving out type deduction for this scenario.
However, as I am trying to adopt the new and cool C++11/14 features I'd like to understand why this in particular does not work as I thought it would.
You can simplify all that code to this:
struct A {
int func();
};
auto A::func() { return 0; }
This is not valid, a function that is declared with a placeholder type must use the placeholder in all declarations:
[decl.spec.auto]/13:
Redeclarations or specializations of a function or function template with a declared return type that uses a placeholder type shall also use that placeholder, not a deduced type.
The following works:
struct MyClass {
auto foo();
};
auto MyClass::foo() {
return 10;
}
Although I dunno why your case shouldn't work.
(with Jonathan answer, now I know)
I'm in the process of refactoring a large class -- let's call it Big -- that has a huge amount of copy-paste code. Much of this copy-paste code exists in switch cases where only the types involved end up being different. The code is switching based on an enum member variable of the class whose value is known only at runtime.
My attempt to fix this involves having a Dispatcher class that looks up appropriately typed functions via a static function called lookup(). The functions that do the actual work are always called go() and have to be defined in a wrapper class template (whose sole parameter is the runtime enum value currently being switched on). The go() functions may or may not be template functions themselves.
Here is a distilled version of the code. My apologies for the length, but this was as short as I could get it without losing important context.
#include <cassert>
class Big
{
public:
enum RuntimeValue { a, b };
Big(RuntimeValue rv) : _rv(rv) { }
bool equals(int i1, int i2)
{
return Dispatcher<Equals, bool(int, int)>::lookup(_rv)(i1, i2);
}
template<typename T>
bool isConvertibleTo(int i)
{
return Dispatcher<IsConvertibleTo, bool(int)>::lookup<T>(_rv)(i);
}
private:
template<RuntimeValue RV>
struct Equals
{
static bool go(int i1, int i2)
{
// Pretend that this is some complicated code that relies on RV
// being a compile-time constant.
return i1 == i2;
}
};
template<RuntimeValue RV>
struct IsConvertibleTo
{
template<typename T>
static bool go(int i)
{
// Pretend that this is some complicated code that relies on RV
// being a compile-time constant.
return static_cast<T>(i) == i;
}
};
template<template<RuntimeValue> class FunctionWrapper, typename Function>
struct Dispatcher
{
static Function * lookup(RuntimeValue rv)
{
switch (rv)
{
case a: return &FunctionWrapper<a>::go;
case b: return &FunctionWrapper<b>::go;
default: assert(false); return 0;
}
}
template<typename T>
static Function * lookup(RuntimeValue rv)
{
switch (rv)
{
case a: return &FunctionWrapper<a>::go<T>;
case b: return &FunctionWrapper<b>::go<T>;
default: assert(false); return 0;
}
}
// And so on as needed...
template<typename T1, typename T2>
static Function * lookup(RuntimeValue rv);
};
RuntimeValue _rv;
};
int main()
{
Big big(Big::a);
assert(big.equals(3, 3));
assert(big.isConvertibleTo<char>(123));
}
This mostly works, except that:
It builds and works fine under Visual C++ 9 (2008), but under GCC 4.8 it results in compilation errors in the function-template overload of lookup().
It requires that a new function-template overload of lookup() be written for every new number of function template parameters that we want to support in go().
It's cumbersome and confusing to use.
Here are the errors that occur under GCC:
Big.cpp: In static member function 'static Function* Big::Dispatcher<FunctionWrapper, Function>::lookup(Big::RuntimeValue)':
Big.cpp(66,65) : error: expected primary-expression before '>' token
case a: return &FunctionWrapper<a>::go<T>;
^
Big.cpp(66,66) : error: expected primary-expression before ';' token
case a: return &FunctionWrapper<a>::go<T>;
^
Big.cpp(67,65) : error: expected primary-expression before '>' token
case b: return &FunctionWrapper<b>::go<T>;
^
Big.cpp(67,66) : error: expected primary-expression before ';' token
case b: return &FunctionWrapper<b>::go<T>;
^
My question is twofold:
Why is this failing to build under GCC, and how do I fix it?
Is there a better (i.e., less cumbersome and confusing) way to do this?
The code has to be compilable under Visual C++ 9 (2008), so I can't use anything C++11-specific.
Since go is a dependent name of a template, you need to use the template disambiguator:
case a: return &FunctionWrapper<a>::template go<T>;
// ^^^^^^^^
case b: return &FunctionWrapper<b>::template go<T>;
// ^^^^^^^^
This tells the compiler to parse what follows the scope resolution operator (::) as the name of a template, and the subsequent angular brackets as delimiters for the template arguments.
Why is this failing to build under GCC, and how do I fix it?
Because GCC is conforming to the Standard, and performs two-phase name lookup, while MSVC delays name lookup until instantiation time and, therefore, knows that go is the name of a template.
Before instantiation this information is not available, because it is impossible to know what T is, and the primary template could be specialized for a given T so that go is not the name of a member function template, but rather of a data member.
This said, I expect MSVC to support the template disambiguator anyway, so adding it should make your program compile both on GCC/Clang/whatever-conforms-to-the-Standard and on MSVC.
I recently wrote a command dispatcher:
#include <map>
// because std::invoke is not in this compiler version.
#define CALL_MEMBER_FN(object,ptrToMember) ((object).*(ptrToMember))
template <class MyType, class cmd_type, class ret_type, typename... Args>
class CommandDispatcher {
typedef ret_type (MyType::*CommandFunction)(Args... args);
public:
// create using static/existing map
CommandDispatcher(std::map<cmd_type, CommandFunction>& cmd_map) : _command_table(cmd_map) {}
ret_type operator()(MyType& my_obj, cmd_type cmd, Args... args)
{
int retval = 0;
if (_command_table.find(cmd) == _command_table.end()) {
std::cerr << "No command implementation found: " << cmd << endl;
return -EINVAL;
}
return CALL_MEMBER_FN(my_obj, _command_table[cmd])(args...);
}
private:
std::map<cmd_type, CommandFunction>& _command_table;
};
Using it looks like:
class MyClass {
public:
MyClass() : _dispatcher(_command_map) {}
private:
static std::map<int, CommandFunction> _command_map;
CommandDispatcher<MyClass, int, int, const char*, int> _dispatcher;
};
And in cpp:
std::map<int, CommandFunction> MyClass::_command_map{
{E_CMD1, &MyClass::Cmd1},
{E_CMD2, &MyClass::Cmd2},
};
Consider the following piece of code:
#include <cstddef>
template<size_t value> class dummy { };
class my_class
{
int m_member;
// Overload 1
template<size_t value>
friend void friend_func(dummy<value>*);
// Overload 2
template<size_t value>
friend void friend_func(int(*)[value]);
};
// Overload 1
template<size_t value>
void friend_func(dummy<value>*)
{
my_class instance;
instance.m_member = value;
}
// Overload 2
template<size_t value>
void friend_func(int(*)[value])
{
my_class instance;
instance.m_member = value;
}
int main(int argc, char* argv[])
{
dummy<5> d;
friend_func(&d); // call Overload 1
int arr[5];
friend_func(&arr); // call Overload 2 - error in MSVC!
return 0;
}
As you can see, the only difference between these two functions is that the second one takes a pointer to value ints instead of dummy<value>.
This code compiles just fine in GCC ($ gcc-4.7.2 test.cpp) and Clang (thanks WhozCraig), but throws the following error in MSVC (I tested 2012):
1>d:\path\to.cpp(32): error C2248: 'my_class::m_member' : cannot access private member declared in class 'my_class'
1> d:\path\to.cpp(8) : see declaration of 'my_class::m_member'
1> d:\path\to.cpp(7) : see declaration of 'my_class'
1> d:\path\to.cpp(40) : see reference to function template instantiation 'void friend_func<5>(int (*)[5])' being compiled
To me this looks like a bug. However, is there anyone who encountered such a behavior before? Is it really a bug, or maybe there's a particular reason for the error? Any quick workaround for this?
Edit: I've been able to find a proper workaround, see answer below.
It's definitely a bug: A template function parametrized on the size of an array cannot be declared as a friend of a class. It occurs when value is deduced as the size of the array for your friend template function. Here is a shortened version of your code that compiles fine. This example is the exact same code as your example except I specified the size of the array.
class my_class
{
int m_member;
template<size_t value>
friend void friend_func(int(*)[5]);
};
template<size_t value>
void friend_func(int(*)[5])
{
my_class instance;
instance.m_member = value;
}
int main()
{
int arr[5];
friend_func<5>(&arr);
}
One workaround it to pass the value as a second function argument:
template <typename T>
void friend_func(T, int value)
{
my_class instance;
instance.m_member = value;
}
Pretty sure it's a known issue with MSVS. Your specific issue is listed in the Portability Hints: Micrsoft Visual C++ on boost.org.
Look for Templates as Friends. I do not know the work around. However, I think you can make a class a friend. You may be able to use that as a work around.
I've figured a workaround that preserves the functionality yet does its job of preventing the error message. The idea is to use a proxy function and a proxy class to carry the pointer to the array and it's size. Here's the solution:
#include <cstddef>
// Workaround class for a bug in MSVC.
// https://connect.microsoft.com/VisualStudio/feedback/details/717749
// http://stackoverflow.com/questions/15149607
template<class element_type, size_t count>
class friend_declaration_bug_workaround
{
public:
typedef element_type(*ptr_type)[count];
private:
ptr_type m_arr;
public:
explicit friend_declaration_bug_workaround(ptr_type arr)
: m_arr(arr)
{
}
ptr_type value() const
{
return m_arr;
}
};
class my_class
{
int m_member;
friend void friend_func(int*);
template<size_t value>
friend void friend_func_workaround(friend_declaration_bug_workaround<int, value>);
};
template<size_t value>
void friend_func_workaround(friend_declaration_bug_workaround<int, value> workaround)
{
my_class instance;
instance.m_member = (*workaround.value())[0];
}
void friend_func(int* arr)
{
my_class instance;
instance.m_member = *arr;
}
template<size_t value>
void friend_func(int(*arr)[value])
{
friend_declaration_bug_workaround<int, value> workaround(arr);
return friend_func_workaround(workaround);
}
int main(int argc, char* argv[])
{
int value;
friend_func(&value); // call non-templated function
int arr[5];
friend_func(&arr); // call workarounded function
return 0;
}
I have a class template with a member function that has a lambda which wants to use a class template parameter type. It fails to compile inside the lambda but succeeds, as anticipated, outside the lambda.
struct wcout_reporter
{
static void report(const std::wstring& output)
{
std::wcout << output << std::endl;
}
};
template <typename reporter = wcout_reporter>
class agency
{
public:
void report_all()
{
reporter::report(L"dummy"); // Compiles.
std::for_each(reports_.begin(), reports_.end(), [this](const std::wstring& r)
{
reporter::report(r); // Fails to compile.
});
}
private:
std::vector<std::wstring> reports_;
};
int wmain(int /*argc*/, wchar_t* /*argv*/[])
{
agency<>().report_all();
return 0;
}
The compilation error:
error C2653: 'reporter' : is not a class or namespace name
Why can't I access the class template parameter type inside the member function lambda?
What do I need to do to gain access to the class template parameter type inside the member function lambda?
This should compile OK as-is. It appears that your compiler has a bug in the name lookup rules in a lambda. You could try adding a typedef for reporter inside report_all.
Use typedef:
template <typename reporter = wcout_reporter>
class agency
{
typedef reporter _myreporter;
public:
void report_all()
{
reporter::report(L"dummy"); // Compiles.
std::for_each(reports_.begin(), reports_.end(), [this](const std::wstring& r)
{
// Take it
agency<>::_myreporter::report(r);
});
}
};
I'm wondering what's the proper syntax for calling template method given as:
struct print_ch {
print_ch(char const& ch) : m_ch(ch) { }
~print_ch() { }
template<typename T>
void operator()() {
std::cout << static_cast<T>(m_ch) << std::endl;
}
private:
char m_ch;
};
I came up with sth like this:
print_ch printer('c');
printer.operator()<int>();
And it seems to work (GCC 4.5), but when I use it inside another templated method, e.g.:
struct printer {
typedef int print_type;
template<typename T_functor>
static void print(T_functor& fnct) {
fnct.operator()<print_type>();
}
};
Compilation fails with error: expected primary-expression before '>' token. Any idea to get it right? Thanks in advance.
You have to tell the compiler explicitly that the operator() of the templated fnct is itself a template:
fnct.template operator()<print_type>();
If you don't specify this with the template keyword the compiler will assume that operator() is just a normal method, not a template.
Since T_functor is itself a template, the compiler (or parser) assumes to know nothing about it's members, so you have to explicetly tell it you are calling a template methode using:
fnct.template operator()<print_type>();