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},
};
Related
I would like to place a std::variant inside a class and return its elements with a template function. Here is an example:
#include <string>
#include <variant>
class Class {
public:
std::variant<std::string, double> cont;
Class() {}
template <class V> Class(const V v) { cont = v; }
template <typename V> V fun() {
if (std::holds_alternative<double>(cont))
return std::get<double>(cont);
else if (std::holds_alternative<std::string>(cont))
return std::get<std::string>(cont);
}
};
int main() {
Class c;
c = 20;
double d = c.fun<double>();
return 0;
}
I try to return the elements of the class Class through the template function fun. However, gcc-9.1 refuses to compile the code and tells me
Class.cpp:12:46: error: cannot convert ‘std::__cxx11::basic_string<char>’ to ‘double’ in return
12 | return std::get<std::string>(cont);
Why is there any attempt to convert the string (the second return type of the function foo) to a double? Can I prevent this and solve the problem? Do I use the std::variant class unidiomatic?
The issue here is that you query the current value stored at runtime, while the function signature of the template instantiation is performed at compile time. Consider how the member function looks like when you try using it to retrieve a double:
double fun() {
if (/* ... */)
return std::get<double>(cont); // Ok return type is double, too
else if (/* ... */)
return std::get<std::string>(cont); // Error, return type should be string?!
}
This can't work. You need to change the way to access the data member, e.g. passing an overload set to std::visit, by providing two getter-like functions returning std::optional<double> and std::optional<std::string> or something similar.
All runtime if branches must be compilable even if not taken. If we call fun() with V == double then returning an std::string makes no sense and causes the error (even if that branch would never be taken, the compiler can't know that for certain).
Instead, just return it right away through V:
template <typename V> V fun() {
if (std::holds_alternative<V>(cont))
return std::get<V>(cont);
return {}; // return default constructed V. You could throw an exception here instead etc.
}
I have a couple of A like classes with method, say, get_value(), which return values of different types. And one class B, which returns a value with method value(). For example:
struct A_like1 {
int get_value() const { return 10; }
};
struct A_like2 {
char get_value() const { return 'a'; }
};
struct B
{
float value() const { return 2.5; }
};
Now, I need a function to retrieve that value for these classes. I have a function:
template<typename T>
auto get_value(const T & t) -> result_of<decltype(&T::get_value)(T)>::type
{
return t.get_value();
}
It gives me a number of compilation errors on VS2010 starting with:
Error 1 error C2061: syntax error : identifier 'type' c:\_data\work\vmxi\c++\vox\vox\template_specialization.h 51
For B I have an overload, which works fine. The question is, how to make get_value() work with result_of<>()?
P.S. Hmm, I have just realized that I could've used -> decltype(T().get_value()) instead, which works fine. But why doesn't result_of<>() work? Besides, I was able to declare a variable in .cpp file:
result_of<decltype(&A_like1::get_value)(A_like1)>::type i=0;
The keyword typename can be used to declare that a dependent name, like std::result_of<T>::type, is a type. Otherwise, I believe it's assumed that std::result_of<T>::type is a data member. In C++14, several type traits have gotten a using alias that includes the typename keyword for you. These alias are always have the same name as the trait, with a _t suffix.
Try :
typename result_of<decltype(&T::get_value)(T)>::type
or, with C++14 :
result_of_t<decltype(&T::get_value)(T)>
I installed the CTP-Nov2013-Compiler to get familiar/experiment with some C++14 features(learning by doing/reading) for VS 2013.
I tried something like a string to any POD-type converter without using generic methods which failed due to the error (cannot spell the correct error beacuse since today I made somehow Visual Studio to crash whenever I try to build the program[CTP bug?]) 'return type is not of the first return type'.
An example of the problem:
#include <iostream>
using namespace std;
enum EType{
eInt,
eBool,
eFloat,
eString
}
class Test
{
Test();
virtual ~Test(){}
decltype(auto) SomeMethod(std::string texttoconvert, EType type)
{
switch(type)
{
//convert to the specific type by using the C++11 stoi* functions and return it (for the example I'm not checking for failure in the conversion)
case eInt: return std::stoi(texttoconvert);
break;
case eFloat: return std::stof(texttoconvert);
break;
...
default:
break;
}
}
int main()
{
Test hugo;
auto lAuto=hugo.SomeMethod("1.234", eFloat);
cout<<lAuto<<endl; //should return a float
return 0;
}
So the question is, is the error of logical kind (except not using try-catch-blocks for the std::sto* conversion) or is it a syntax error?
Another problem I have, is that I had to implement the method in the header file(else I got an error) and not in the .cpp file, is this a wanted/necessary feature like for template functions?
It's semantic error. auto return type means the method has automatically deduced return type but the type is still one for the whole method, it cannot change based on the invoked return expression. Moreover, C++14 requires that all return expressions return the same type and forbids implicit conversions in this case.
You need to pass compile-time information as a template parameter.
And type determining information must be compile-time information.
enum EType{
eInt,
eBool,
eFloat,
eString
};
template<EType type>
decltype(auto) convert(std::string texttoconvert);
template<>
decltype(auto) convert<eInt>(std::string texttoconvert) {
return std::stoi(texttoconvert);
}
template<>
decltype(auto) convert<eFloat>(std::string texttoconvert) {
return std::stof(texttoconvert);
}
struct Test {
template<EType type>
decltype(auto) SomeMethod(std::string texttoconvert) {
return convert<type>(texttoconvert);
}
};
int main() {
Test t;
float f = t.SomeMethod<eFloat>("3.14");
std::cout << f << "\n";
}
live example.
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 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.