I am very new to C++ and I was trying out templating to understand how it works. I have a template function that accepts an argument of type T. The problem that I am facing is that T's type is determined at runtime depending on the value of T and the compiler throws an error because it determines the type without considering the if-else-if-else block.
#include <iostream>
using namespace std;
class MyClass {
public:
void setInt(int x) {}
void setString(string y) {} // copy string object
};
void f1() {
cout << "break" << endl;
}
template<typename T, typename... Args>
void f1(T arg, Args... args) {
string _type(typeid(arg).name());
cout << __PRETTY_FUNCTION__ << endl;
cout << _type << endl;
MyClass c1;
if( _type.compare("i") == 0 ) {
c1.setInt(arg);
} else if ( _type.compare("PKc") == 0 ) {
//c1.setString(arg);
}
f1(args...);
};
int main() {
f1(7, 3.3, "asd", 0xa1);
return 0;
}
The output:
prog.cpp: In instantiation of ‘void f1(T, Args ...) [with T = const char*; Args = {int}]’:
prog.cpp:30:4: recursively required from ‘void f1(T, Args ...) [with T = double; Args = {const char*, int}]’
prog.cpp:30:4: required from ‘void f1(T, Args ...) [with T = int; Args = {double, const char*, int}]’
prog.cpp:35:24: required from here
prog.cpp:25:13: error: invalid conversion from ‘const char*’ to ‘int’ [-fpermissive]
c1.setInt(arg);
^~~
prog.cpp:6:19: note: initializing argument 1 of ‘void MyClass::setInt(int)’
void setInt(int x) {}
~~~~^
https://ideone.com/the4AP (The link to online compiler)
One possible approach:
void f1_helper(int arg, MyClass* c) {
c->setInt(arg);
}
void f1_helper(string arg, MyClass* c) {
c->setString(arg);
}
template<typename... Args>
void f1(Args... args) {
MyClass c1;
auto _ = {(f1_helper(args, &c1), 0) ...};
}
Demo
Related
I have the following c++ code
#include <iostream>
template<typename Func>
class Foo
{
private:
Func func;
public:
Foo(Func func) : func(func) {}
template<typename T>
Func wrap()
{
Func clbk = func;
auto wrapperCB = [clbk](T t) {
auto job = [clbk, t](){
clbk(t);
};
job();
};
return wrapperCB;
}
template<typename T>
void call(T t)
{
func(t);
}
};
int main()
{
int m = 2;
auto f = [](int & p) {std::cout << "test success " << p << "\n";};
auto obj = std::make_shared<Foo<std::function<void(int &)>>>(f);
auto wrapper = obj->template wrap<int &>();
wrapper(m);
return 0;
}
This is giving compilation error
tsavs-mbp:p utsagarw$ clear; g++ -std=c++11 a.cpp -o z; ./z
a.cpp:18:17: error: no matching function for call to object of type 'const std::__1::function<void (int &)>'
clbk(t);
^~~~
a.cpp:38:32: note: in instantiation of function template specialization 'Foo<std::__1::function<void (int &)> >::wrap<int &>' requested here
auto wrapper = obj->template wrap<int &>();
^
/Applications/Xcode_10.1/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/functional:1677:9: note: candidate function not viable: 1st argument ('const int') would lose const qualifier
_Rp operator()(_ArgTypes...) const;
^
1 error generated.
I don't understand this error. Where did this const come from?
It is building successfully if in wrap I don't create job functor and call clbk directly. What is this job doing to type T?
template<typename T>
Func wrap()
{
Func clbk = func;
auto wrapperCB = [clbk](T t) {
clbk(t);
};
return wrapperCB;
}
If you want to modify any captured variable inside lambda you have to specify it as mutable.
t variable is captured by copy, so you can only read it:
auto job = [clbk, t]() // <-- t passed by copy
{
clbk(t); // clbk takes t by reference -> int&
};
your callback, clbk has signature int& so it means it could modify t. What is not allowed.
Solution:
auto job = [clbk, t]() mutable // keyword 'mutable' added
{
clbk(t); // clbk can change t
};
or make function taking const int& as parameter - then t can be only read.
Demo
I am following this code snippet which makes it easier to pass a member function to an interface expecting a C-style callback (that is, the interface expects a function pointer to the callback, and a void* pointer to user data which will in turn be passed to the callback). Effectively I want to convert Helper::M to Helper::V below.
I am trying to modify the snippet to automatically deduce the template parameters. Here is my current attempt.
#include <iostream>
template <typename R, typename T, typename... Args>
struct Helper {
using V = R (*)(void*, Args...);
using M = R (T::*)(Args...);
template <M m>
static R Fn(void* data, Args... args) {
return (static_cast<T*>(data)->*m)(std::forward<Args...>(args...));
}
};
template <typename R, typename T, typename... Args>
typename Helper<R, T, Args...>::V Cast(R (T::*m)(Args...)) {
return Helper<R, T, Args...>::template Fn<m>;
}
int CIntf(void* data, int (*f)(void*, int)) { return f(data, 1); }
struct UserData {
int x;
int Add(int y) { return x + y; }
};
int main(int argv, char** argc) {
UserData data = {4};
// Explicit parameters; works.
std::cout << CIntf(&data, Helper<int, UserData, int>::Fn<&UserData::Add>)
<< "\n";
// Deduced parameters; fails.
std::cout << CIntf(&data, Cast(&UserData::Add)) << "\n";
return 0;
}
I tried to compile with gcc -std=c++11 -lstdc++. The explicit parameters method works fine, but the deduced parameters method gives the following error:
tmp.cc: In instantiation of ‘typename Helper<R, T, Args>::V Cast(R (T::*)(Args ...)) [with R = int; T = UserData; Args = {int}; typename Helper<R, T, Args>::V = int (*)(void*, int)]’:
tmp.cc:30:58: required from here
tmp.cc:15:42: error: no matches converting function ‘Fn’ to type ‘using V = int (*)(void*, int) {aka int (*)(void*, int)}’
return Helper<R, T, Args...>::template Fn<m>;
^~~~~
tmp.cc:8:12: note: candidate is: template<int (UserData::* m)(int)> static R Helper<R, T, Args>::Fn(void*, Args ...) [with R (T::* m)(Args ...) = m; R = int; T = UserData; Args = {int}]
static R Fn(void* data, Args... args) {
Note that it correctly deduced the template parameters, but failed to convert Helper<int, UserData, int>::Fn<m> to int (*)(void*, int); why? This same conversion succeeded in the explicit case (unless m is somehow different from &UserData::Add).
Unfortunately you'll have to use a macro for this:
#define makeFunc(method) &Helper<decltype(method)>::Fn<method>
And redefine your helper like this for it to work:
template <typename T>
struct Helper;
template <typename R, typename T, typename... Args>
struct Helper<R(T::*)(Args...)>
The reason why you can't use deduction for this, is that deduction only works on function arguments which are run-time values. And you need to use a method's address as template argument which should be a compile-time value.
So when you do this:
return Helper<R, T, Args...>::template Fn<m>;
you are passing a run-time value m as a template argument which is impossible.
For reference, here is the complete code using the macro. Also note the use of std::forward in the original code was incorrect for multiple arguments (see this answer).
#include <iostream>
#include <utility>
template <typename T>
struct Helper;
template <typename R, typename T, typename... Args>
struct Helper<R (T::*)(Args...)> {
template <R (T::*m)(Args...)>
static R Fn(void* t, Args... args) {
return (static_cast<T*>(t)->*m)(std::forward<Args>(args)...);
}
};
#define VOID_CAST(m) &Helper<decltype(m)>::Fn<m>
struct UserData {
int x;
int Add1(int y) { return x + y; }
int Add2(int y, int z) { return x + y + z; }
};
int Call1(void* data, int (*f)(void*, int)) { return (*f)(data, 1); }
int Call2(void* data, int (*f)(void*, int, int)) { return (*f)(data, 1, 2); }
int main() {
UserData data = {4};
std::cout << Call1(&data, VOID_CAST(&UserData::Add1)) << "\n";
std::cout << Call2(&data, VOID_CAST(&UserData::Add2)) << "\n";
return 0;
}
In C++11, I implement function template specialization for identifying inheritance, but it occurred compile-time errors.
f() checks whether the specified class is derived from Base or not.
Following is a source code.
#include <iostream>
#include <type_traits>
using namespace std;
struct Base {};
struct Derived : Base {};
struct Base2 {};
template<typename T, bool = std::is_base_of<Base, T>::value>
void f() {
cout << "T is not Base or Base-derived class." << endl;
};
template<typename T>
void f<T, true>() {
cout << "T is Base or Base-derived class." << endl;
};
int main() {
f<Base>(); // ok
f<Derived>(); // ok
f<Base2>(); // not ok
return 0;
}
Following is error messages.
prog.cpp:15:17: error: non-class, non-variable partial specialization 'f<T, true>' is not allowed
void f<T, true>() {
^
prog.cpp: In function 'int main()':
prog.cpp:20:13: error: call of overloaded 'f()' is ambiguous
f<Base>();
^
prog.cpp:10:6: note: candidate: void f() [with T = Base; bool <anonymous> = true]
void f() {
^
prog.cpp:15:6: note: candidate: void f() [with T = Base]
void f<T, true>() {
^
prog.cpp:21:16: error: call of overloaded 'f()' is ambiguous
f<Derived>();
^
prog.cpp:10:6: note: candidate: void f() [with T = Derived; bool <anonymous> = true]
void f() {
^
prog.cpp:15:6: note: candidate: void f() [with T = Derived]
void f<T, true>() {
^
prog.cpp:22:14: error: call of overloaded 'f()' is ambiguous
f<Base2>();
^
prog.cpp:10:6: note: candidate: void f() [with T = Base2; bool <anonymous> = false]
void f() {
^
prog.cpp:15:6: note: candidate: void f() [with T = Base2]
void f<T, true>() {
^
How can I solve it?
When std::is_base_of<Base, T>::value evaluates true you have two functions with same signature. Therefore you get error "call ... is amibguous".
Try simple overloading as one of the solutions:
namespace detail {
void doIt(std::false_type) {
cout << "T is not Base or Base-derived class." << endl;
};
void doIt(std::true_type) {
cout << "T is Base or Base-derived class." << endl;
};
}
template<typename T>
void f() {
detail::doIt(typename std::is_base_of<Base, T>::type());
};
Of course the function detail::doIt() can be more complex and templated by T.
EDIT: add "detail::" into a function f() call.
Recently, I've been working on a little project alongside my c++ game-dev engine : it's a programming language, written in C++, in one header, named kickC. Here is what I have done so far : (See question below)
#ifndef KICK_C_INCLUDED_H
#define KICK_C_INCLUDED_H
#include <iostream>
#include <string>
#include <sstream>
#include <algorithm>
#include <cctype>
#include <exception>
#include <functional>
#include <unordered_map>
#include <vector>
#define LOG(x) std::cout << x << std::endl;
namespace strutil
{
inline unsigned CountWords(const std::string& value){
std::string temp = value;
std::replace_if(temp.begin(), temp.end(), std::ptr_fun<int, int>(std::isspace), ' ');
temp.erase(0, temp.find_first_not_of(" "));
if(temp.empty())
return 0;
return std::count(temp.begin(), std::unique(temp.begin(), temp.end()), ' ') + !std::isspace(*value.rbegin());
}
}
class KickCException : std::exception
{
public:
explicit KickCException(const char* msg, bool fatal = false)
: msg_(msg){}
explicit KickCException(const std::string& msg)
: msg_(msg){}
virtual ~KickCException() throw(){}
virtual const char* what() const throw(){
return std::string("[error :] [")
.append(msg_)
.append("]")
.c_str();
}
protected:
std::string msg_;
};
class KickCFileException : KickCException
{
public:
explicit KickCFileException(const char* msg)
: KickCException(msg){}
explicit KickCFileException(const std::string& msg)
: KickCException(msg){}
virtual ~KickCFileException() throw(){}
const char* what() const throw() override{
return std::string("[file error :] [")
.append(msg_)
.append("]")
.c_str();
}
};
class KickCEmptyStringException : KickCException
{
public:
explicit KickCEmptyStringException(const char* msg)
: KickCException(msg){}
explicit KickCEmptyStringException(const std::string& msg)
: KickCException(msg){}
virtual ~KickCEmptyStringException() throw(){}
const char* what() const throw() override{
return std::string("[empty string error :] [")
.append(msg_)
.append("]")
.c_str();
}
};
class KickCAPIBehaviourImplementation
{
public:
KickCAPIBehaviourImplementation(){}
~KickCAPIBehaviourImplementation(){}
void AddDefined(const std::string& str, std::function<void(void)> func){
m_values[str] = func;
}
void ParseAndApplyLine(const std::string& line){
std::istringstream iss(line);
for(unsigned i = 0; i < strutil::CountWords(line); ++i){
static std::string word = "";
iss >> word;
for(auto it_map = m_values.begin(); it_map != m_values.end(); ++it_map){
if(it_map->first == word)
{
(it_map->second)(/*HERE ! GIVE SOME ARGUMENTS ! */);
}
}
}
}
private:
std::unordered_map<std::string, std::function<void(void)>> ///so far, args is void... m_values;
};
#endif //KICK_C_INCLUDED_H
///src
int main(int argc, const char** args){
std::ifstream file("script.kick");
KickCAPIBehaviourImplementation kickCApiBehaviour;
try{
if(!file.is_open())
throw KickCFileException("unvalid fileName taken at input");
kickCApiBehaviour.AddDefined("print", [&](void){std::cout << "print found !" << std::endl;});
while(!file.eof()){
std::string line;
std::getline(file, line);
kickCApiBehaviour.ParseAndApplyLine(line);
}
}catch(KickCException& e){
LOG(e.what());
}
file.close();
std::cin.get();
}
So here is the Question : I would like to pass std::function (see class KickCAPIBehaviourImplementation ) a variable argument of types : I need to use variatic templates, of course, but the question how can I implement it so i end up calling my functions like this ?
kickCApiBehaviour.AddDefined("print", [&](int arg1, char * arg2, int arg3){std::cout << arg1 << arg2 << arg3 << std::endl;});
Move the parser into the std::function.
Where you add the function, include a signature:
// helper type:
template<class T>struct tag{using type=T;};
kickCApiBehaviour.AddDefined(
"print", // the name
tag<void(int,char*,int)>{}, // the signature
[&](int arg1, char * arg2, int arg3){
std::cout << arg1 << arg2 << arg3 << std::endl;
} // the operation
);
store a std::function< error_code(ParserState*) >. Inside AddDefined, store a lambda that includes a call to the code that parses arguments and calls the passed in lambda:
template<class R, class...Args, class F>
void AddDefined(std::string name, tag<R(Args...)>, F f) {
std::function< error_code(ParserState*) > r =
[f](ParserState* self)->error_code {
// here, parse each of Args... out of `self`
// then call `f`. Store its return value,
// back in `self`. If there is a parse error (argument mismatch, etc),
// return an error code, otherwise return no_error
};
m_values[name] = r;
};
then m_values contains the operation "take a parser state, and parse the arguments, and call the function in question on them".
Using gcc-4.8 with -std=c++11 I want to create a template function with one behaviour for enums and other behaviour for all other types. I try this
#include <type_traits>
#include <iostream>
template<class T, class = typename std::enable_if<std::is_enum<T>::value>::type>
void f(T& /*t*/)
{
std::cout << "enum" << std::endl;
}
template<class T, class = typename std::enable_if<!std::is_enum<T>::value>::type>
void f(T& /*t*/) {
std::cout << "not enum" << std::endl;
}
enum class E
{
A,
B
};
int main()
{
E e;
f(e);
return 0;
}
but compiler returns
1.cpp:11:6: error: redefinition of ‘template<class T, class> void f(T&)’
void f(T& /*t*/) {
^
1.cpp:5:6: error: ‘template<class T, class> void f(T&)’ previously declared here
void f(T& /*t*/)
^
I can comment out first template, it leads to compile error, and it's expectable.
And I also can comment out second template, in this case code code can be compiled.
What do I do wrong?
Because compiler sees them as the same function template, instead, you should do this:
#include <type_traits>
#include <iostream>
template<class T, typename std::enable_if<std::is_enum<T>::value, bool>::type = true>
void f(T& /*t*/)
{
std::cout << "enum" << std::endl;
}
template<class T, typename std::enable_if<!std::is_enum<T>::value, bool>::type = true>
void f(T& /*t*/) {
std::cout << "not enum" << std::endl;
}
enum class E
{
A,
B
};
int main()
{
E e;
f(e);
return 0;
}