Templated member function and inheritence - c++

I have a template member function declared in a class that call the correct member function depending on type, and want to add some functionality to it in a daughter class, by adding a member function, like in the main.cpp example below :
#include <iostream>
class A
{
public:
template <typename T>
void handleSocketData(const T& t)
{
handleData(t);
}
void handleData(int data)
{
std::cout << data << std::endl;
}
};
class B: public A
{
public :
void handleData(std::string data) const
{
std::cout << data << std::endl;
}
};
int main(int argc, char *argv[])
{
A a;
B b;
a.handleSocketData<int>(30);
b.handleSocketData<std::string>("Hi");
return 0;
}
My problem is that b.handleSocketData<QString>("Hi"); actually does generate a new template instance in A class as shown in the output of command /usr/bin/clang++ -DQT_CORE_LIB -isystem /usr/include/qt6/QtCore -isystem /usr/include/qt6 -isystem /usr/lib64/qt6/mkspecs/linux-g++ -g -std=gnu++17 -Xclang -ast-print -fsyntax-only main.cpp:
class A {
public:
template <typename T> void handleSocketData(const T &t) {
this->handleData(t);
}
template<> void handleSocketData<int>(const int &t) {
this->handleData(t);
}
template<> void handleSocketData<std::basic_string<char>>(const std::basic_string<char> &t) {
<recovery-expr>(this->handleData, t);
}
void handleData(int data) {
std::cout << data << std::endl;
}
};
class B : public A {
public:
void handleData(std::string data) const {
std::cout << data << std::endl;
}
};
int main(int argc, char *argv[]) {
A a;
B b;
a.handleSocketData<int>(30);
b.handleSocketData<std::string>("Hi");
return 0;
}
So right now I have a compilation error, saying that no function handleData(const std::string& data) is found, which is normal.
A workaround we've found is to define a two-arguments template, taking the daughter class as argument (kind of visitor pattern) :
#include <iostream>
class A
{
public:
template <typename T, typename U>
void handleSocketData(U& u, const T& t)
{
u.handleData(t);
}
void handleData(int data)
{
std::cout << data << std::endl;
}
};
class B: public A
{
public :
void handleData(std::string data)
{
std::cout << data << std::endl;
}
};
int main(int argc, char *argv[])
{
A a;
B b;
a.handleSocketData<int>(a, 30);
b.handleSocketData<std::string>(b, "Hi");
return 0;
}
What do you think ? Is there a cleaner way ?

This looks like a classic use case for CRTP. You can make A a template over a derived class Derived and then dispatch function calls to the derived class via a static_cast. For this to work, any derived class Derived must be derived from A<Derived>.
Since you seem to want to use A as a non-abstract class, you would have to add a default derived class marking it as "final". In the following code, the empty struct FinalTag serves this purpose.
#include <iostream>
struct FinalTag;
template <typename Derived=FinalTag>
class A
{
public:
template <typename T>
void handleSocketData(const T& t)
{
cast().handleData(t);
}
void handleData(int data)
{
std::cout << data << std::endl;
}
private:
constexpr auto& cast() {
return static_cast<Derived&>(*this);
}
};
struct FinalTag : A<FinalTag> {};
class B: public A<B>
{
public :
using Base = A<B>;
using Base::handleData;
void handleData(std::string data)
{
std::cout << data << std::endl;
}
};
int main(int argc, char *argv[])
{
A a;
B b;
a.handleSocketData(30);
b.handleSocketData("Hi");
// this only works if you bring in Base::handleData in the
// derived class
b.handleSocketData(30);
return 0;
}
Live Code: https://godbolt.org/z/ns9aPjG76
This is a prototype. You would want to add a const version to the cast method for instance.
Edit:
As Jarod42 pointed out in the comments, C++23 really simplifies CRTP with "deducing this": https://godbolt.org/z/cGzMrnEhc. This isn't currently widely supported by compilers though.

A slightly different version of CRTP to the one suggested by Joerg Brech could be more suitable in some cases.
#include <iostream>
class A
{
public:
template <class Class, typename T>
void handleSocketData(const T& t)
{
static_cast<Class*>(this)->handleData(t);
}
void handleData(int data)
{
std::cout << data << std::endl;
}
};
class B: public A
{
public :
void handleData(std::string data) const
{
std::cout << data << std::endl;
}
};
int main(int argc, char *argv[])
{
A a;
B b;
a.handleSocketData<A, int>(30);
b.handleSocketData<B, std::string>("Hi");
return 0;
}
It is very similar to your solution in the sense that we instruct handleSocketData which class it should use to call handleData from. The only difference is that the decision is made not dynamically but at compile time.

Related

How can I return an arbitrary derived class of an abstract generic class and call its generic methods?

I've got an abstract class that uses variable template.
template <class T>
class Abstract
{
public:
virtual void print(T t) = 0;
};
There can be any derivatives of the class like so:
class A : public Abstract<std::string>
{
public:
void print(std::string str)
{
std::cout << str << std::endl;
}
};
class B : public Abstract<int>
{
public:
void print(int number)
{
std::cout << std::to_string(number) << std::endl;
}
};
Now I want a function to return one of these derivatives so I can execute the print method. And here is my Problem:
template (class T); // error here
Abstract<T> &f(int n) // what should the return type look like?
{
if (n == 0)
{
A a{};
return a;
}
else
{
B b{};
return b;
}
}
int main()
{
A a{f(0)};
a.print("foo");
B b{f(1)};
b.print(42);
return 0;
}
So how is it be possible to return a class with unknown parameter type and call its methods?
I already tried returning derived classes without templates which works fine. As soon as templates are added code wont compile. I also tried void* and reinterpret_cast. Problem here is that I have manually to decide to what type to cast to.
So how can I return an arbitrary superclass of an abstract generic class and call its generic methods?
I think inheritance is the wrong approach here. Instead I would use specialization instead:
template<typename T>
struct Foo;
template<>
struct Foo<std::string>
{
void print(std::string const& s)
{
std::cout << s << '\n';
}
};
template<>
struct Foo<int>
{
void print(int value)
{
std::cout << value << '\n';
}
};
Then you don't need a selector to pick the object to create, just the correct type:
int main()
{
Foo<std::string> f1;
f1.print("hello");
Foo<int> f2;
f2.print(123);
}
If you really need a factor function, then it could be created like this:
template<typename T>
Foo<T> create()
{
return Foo<T>();
}
And use like
int main()
{
auto f1 = create<std::string>();
f1.print("hello");
auto f2 = create<int>();
f2.print(123);
}

C++ function specialization from class template

I have a class, classB which has several functions which I would like to specialize based on an enumerator template S.
I have the following example:
#include <iostream>
#include <string>
#include <array>
typedef std::array<double, 3> vec;
enum Op {Op1, Op2, Op3};
template<class T, enum Op S=Op1>
class classA
{
public:
class innerClassA
{
public:
void foo() const
{
std::cout <<"Operation 1" << std::endl;
}
};
};
template<class T>
class classB
{
public:
template <Op S = Op1>
void myFunc()
{
typename classA<T, S>::template innerClassA myInnerClassObj;
for (int i = 0; i < 10; i++)
myInnerClassObj.foo();
}
// Other functions the I would like to able to speciallize afterwards based on template S
void myFunc2() { std::cout << "Func 2" << std::endl; }
void myFunc3() { std::cout << "Func 3" << std::endl; }
};
template<>
void classA<vec, Op2>::innerClassA::foo() const
{
std::cout << "Operation 2" << std::endl;
}
template<>
void classA<vec, Op3>::innerClassA::foo() const
{
std::cout << "Operation 3" << std::endl;
}
int main(int argc, char** argv)
{
classB<vec> obj;
obj.myFunc();
obj.myFunc2();
obj.myFunc<Op2>();
obj.myFunc<Op3>();
return 0;
}
In the above example. The function myFunc has a template parameter based on the enumerator. In the main function, I can call the specialized version based on the value of the enumerator. I would also like to do the same for the other functions,myFunc2 however, always having to put:
template <Op S = Op1>
someFunction()
is quite bothersome. Is there any other way to specify that all functions in a class have a default template based on the enumerator?
Kind regards
No, there is not, apart from macros.
#define OPFUNC template<Op S = Op1> void
OPFUNC myFunc() {}
OPFUNC myFunc2() {}
...
#undef OPFUNC

how to add member-variable for a specialized version of a template class?

I have a template class, and at least 95% codes of it is same for all types of the template-parameter, unless a member-variable and a function should be added for one specialization.
The sample I want to get is following:
template <typename T>
class AClass {
private:
T payLoad;
public:
AClass( const T& crp_payload ) : payLoad( crp_payload ) {};
void showMe() {
cout << "Common showMe:" << payLoad << endl;
};
/*
* A lot of functions same for all specializations go here.
* I absolutely don't want to implement respectively them for
* every specializations!
*/
// specializing for int ----------------------------
// dedicated function
template <int>
void showPayload() {
cout << "AClass<int>::showPayload:" << payLoad << endl;
};
// dedicated variable, but following code can not be compiled!
template <int>
int otherPayload;
};
int main() {
AClass<int> iac( 123 );
iac.showMe();
iac.showPayload();//can not pass the compiling!
AClass<float> fac(456);
fac.showMe();
return 0;
};
My questions:
How to add merely "otherPayload" variable without re-coding entire
AClass<int>?
How to call showPayload() sinc I get a error msg when I
do it in main() as above.
Is there no way only by specializing to "revise/supplement" some
members to a class without totally re-implement it?
One possible way would be the good old inheritance:
template<class T> struct Extra {};
template<> struct Extra<int> {
int extraPayload;
void showPayload();
};
template<class T> class Class: public Extra<T> {
void showMe();
};
template<> void Class<int>::showMe() { showPayload(); }
All the specialization-specific parts are extracted in a separate class, and common methods are specialized as needed.
I think you can simply do normal specialization of the template-class:
#include <iostream>
#include <iomanip>
template <typename T>
class BaseClass
{
protected:
T payLoad;
public:
BaseClass(const T& crp_payload)
: payLoad( crp_payload )
{ }
void showMe() {
std::cout << "Common showMe:" << payLoad << std::endl;
}
/*
* A lot of functions same for all specializations go here.
* I absolutely don't want to implement respectively them for
* every specializations!
*/
};
template <typename T>
class AClass
: public BaseClass<T>
{
public:
AClass( const T& crp_payload )
: BaseClass<T>(crp_payload)
{ }
};
// specializing for int ----------------------------
template<>
class AClass<int>
: public BaseClass<int>
{
public:
AClass( int crp_payload )
: BaseClass(crp_payload)
{ }
// dedicated function
void showPayload() {
std::cout << "AClass<int>::showPayload:" << payLoad << std::endl;
}
private:
int otherPayload;
};
int main() {
AClass<int> iac( 123 );
iac.showMe();
iac.showPayload();//can not pass the compiling!
AClass<float> fac(456);
fac.showMe();
return 0;
}

Call function using subtype overload

Consider the following program
class A {};
class B : public A {};
void fun(A v) { std::cout << "A" << std::endl; }
void fun(B v) { std::cout << "B" << std::endl; }
void call(A v) { fun(v); }
int main(int argc, char *argv[]) {
A a;
B b;
call(a);
call(b);
fun(a);
fun(b);
}
It will print
A
A
A
B
Is there a way for the program to notice that the variable is actually a B in the second case, and hence call the overloaded fun(B), so that the output would become the following instead?
A
B
A
B
Option 1
You could use a template for your call function.
#include <iostream>
class A {};
class B : public A {};
void fun(A v) { std::cout << "A" << std::endl; }
void fun(B v) { std::cout << "B" << std::endl; }
template <typename T>
void call(T v) { fun(v); }
int main(int argc, char *argv[]) {
A a;
B b;
call(a);
call(b);
fun(a);
fun(b);
}
This will only compile if there is an overload of fun that takes a parameter of type T, which in your case is A or B.
Working example
Option 2
Alternatively, you could make these free functions into virtual class methods and actually use polymorphism.
#include <iostream>
class A
{
public:
virtual void fun() { std::cout << "A" << std::endl; }
void call() { fun(); }
};
class B : public A
{
public:
virtual void fun() override { std::cout << "B" << std::endl; }
};
int main(int argc, char *argv[]) {
A a;
B b;
a.call();
b.call();
a.fun();
b.fun();
}
Working example
Is there a reason you don't want fun to be part of the classes? If fun was a virtual method on the class, and call took an A and simply did v.fun() it would find the proper implementation to execute

automatic conversion of bool to nullptr_t

I have the following code with a custom Variant class and a custom SmartPtr class:
using namespace std;
class Object
{
public:
};
template<typename T>
class SmartPtr
{
public:
template<typename Y>
explicit SmartPtr(Y* p) { p_ = p; }
SmartPtr(std::nullptr_t) { p_ = nullptr; }
private:
T* p_;
};
class Variant
{
public:
Variant(bool b) : _b(b) { }
private:
bool _b;
};
class Obj
{
public:
void test(SmartPtr<Object> /*p*/) { cout << "smartptr version!" << endl; }
void test(Variant /*v*/) { cout << "variant version!" << endl; }
};
int main(int argc, const char *argv[])
{
Obj o;
o.test(nullptr); // calls SmartPtr version
o.test(true); // calls Variant version
o.test(false); // -> compiler error: ambiguous call to overloaded function
return 0;
}
I assume that the boolean false can be converted both to the Variant and to 0 then to nullptr and then to SmartPtr, which causes this error.
Any chances of avoiding this conversion?
For the user of the library an API which works with 'o.test(true);' but requires something like 'o.test(Variant(false));' to compile is not very intuitive.
I believe I have an ideal solution. It only requires that the test function be altered, so it leaves SmartPtr and Variant alone, which is ideal. It adds a non-defined templated overload to test that has specializations for bool and nullptr that are defined. This directly dispatches bool and nullptr to the desired specialization, but causes link errors on other unhandled types. I'm so glad to have this worked out because I've certainly run into this in many forms myself. I wish you could use explicit of function parameters!!
I got the idea from here: C++ templates that accept only certain types
using namespace std;
class Object
{
public:
};
class Variant
{
public:
Variant( bool b) : _b(b) { }
private:
bool _b;
};
template<typename T>
class SmartPtr
{
public:
SmartPtr(std::nullptr_t null) { p_ = nullptr; }
template<typename Y>
SmartPtr(Y* p) { p_ = p; }
private:
T* p_;
};
class Obj
{
public:
void test(SmartPtr<Object> here /*p*/) {
cout << "smartptr version!" << endl;
}
void test(Variant /*v*/) { cout << "variant version!" << endl; }
template<typename T> void test(T t);
template<>
void test<bool>(bool b) {
cout << "bool specialization" << endl;
test(Variant(b));
}
template<>
void test<std::nullptr_t>(std::nullptr_t null) {
cout << "nullptr specialization" << endl;
test(SmartPtr<Object>(nullptr));
}
};
int main(int argc, const char *argv[])
{
Obj o;
Obj c;
Object object;
//o.test(3); // Gives link error LNK2019
o.test(Variant(true)); // calls Variant version
o.test(SmartPtr<Object>(&object)); // calls SmartPtr version
o.test(nullptr); // dispatched to SmartPtr version by nullptr specialization
o.test(true); // dispatched to Variant version by bool specialization
o.test(false); // dispatched to Variant version by bool specialization
return 0;
}
I had already answered with something not ideal, so I leave that answer in tact as what follows:
=============================================
I don't have an ideal solution here, and I don't know the constraints you have on your code so this may not be of functional use to you, but the following is sensible. It disallows code to use nullptr at compile time and relies on a global null_smart constant to be used in all cases where the caller is simply showing no interest in passing an object.
#include <iostream>
using namespace std;
class Object
{
public:
};
class Variant
{
public:
Variant(bool b) : _b(b) { }
private:
Variant(std::nullptr_t) {};
private:
bool _b;
};
template<typename T>
class SmartPtr
{
public:
SmartPtr() { p_ = nullptr; }
template<typename Y>
SmartPtr(Y* p) { p_ = p; }
private:
T* p_;
};
class Obj
{
public:
void test(SmartPtr<Object> /*p*/) { cout << "smartptr version!" << endl; }
void test(Variant /*v*/) { cout << "variant version!" << endl; }
};
const SmartPtr<Object> null_smart;
int main(int argc, const char *argv[])
{
Obj o;
o.test(null_smart); // calls SmartPtr version, without interest in passing object
o.test(true); // calls Variant version
o.test(false); // calls Variant version
return 0;
}
It's cleaner than the true/Variant(false) issue, but still a bit on the picky side.