The question:
Is there a way to call the "base" template function from a specialized template function in C++, the way a child class can access the parent's versions of virtual methods when overriding them? (NOTE: I suspect the answer is "no" but would love to be wrong)
The context:
I often find myself specializing a template function only because a special case needs extra pre- or post-processing, not because the "guts" of the code have changed.
To give a contrived example:
With inheritance, you can do the following:
struct base {
virtual void go() { printf("%p", this); }
};
struct foo : base {
virtual void go() { printf("this foo lives at "); base::go(); }
};
... and calling foo::go() will print "this foo lives at <address>"
With templates, though:
template <typename T>
void go(T const &t) { printf("%p\n", &t); }
template <>
void go(foo const &f) {
printf("this foo lives at ");
??? how to access "base" template ???
}
You can work around the problem in an ugly way by factoring out a mess of little helper functions and specializing them instead of the function you actually care about:
template <typename T>
void _go_pre(T const &t) { /* do nothing */ }
template <typename T>
void _go_post(T const &t) { /* do nothing */ }
template <typename T>
void go(T const &t) {
_go_pre(t); /* just in case */
printf("%p\n", &t);
_go_post(t);
}
template<>
void _go_pre(foo const &t) { printf("this foo lives at "); }
... but that clutters the code significantly because now the "base" template needs to anticipate all the ways a "child" specialization might override it, and most types will use few, if any, of those hooks. The clutter becomes unreadable and unmaintainable pretty quickly, because the reason for these hooks is not known at the point where they are defined, and you have to test the different combinations of used/unused hooks.
All this is exactly like the problems you'd have with virtual method overrides in a world where a child class couldn't access original version provided by the parent class.
It's not possible directly. However, you can do with fewer (and IMO less ugly) helpers like this:
template <typename T>
void base_go(T const &t) { printf("%p\n", &t); }
template <typename T>
void go(T const &t) { base_go(t); }
template<>
void go(foo const &t) { printf("this foo lives at "); base_go(t); }
As an alternative, you could put the base_ variants into a separate namespace instead of giving them modified names.
If your foo is a normal type you can simple create a (non-template) function overload and call the templated version inside:
#include <iostream>
struct A {};
template<class T>
void f(T const&) {
std::cout << "in f<T>(T const&)" << std::endl;
}
void f(A const& a) {
std::cout << "in f(A const&)" << std::endl;
f<A>(a);
}
int main() {
A a;
f(a);
return 0;
}
Related
In a templated class, can we check a member function's signature and define different compilation behaviors for different subclasses? To be more specific, consider the following simple example:
template <typename T>
class Foo {
// ingore all other functions
virtual std::shared_ptr<T> do_something(int a) {
return std::make_shared<T>(a);
}
};
This should just work fine with the T1 class:
class T1 {
T1(int a) {
// implememntation
}
// ingore all other functions
};
// Foo<T1> would just work
However, it will fail the compilation with T2 because Foo's do_something function is clearly implemented to call T's constructor with one argument only:
class T2 {
T2(int a, int b) {
// implememntation
}
// ingore all other functions
};
// Foo<T2> would fail to compile
So the question is, can we rework on Foo to let it work for both T1 and T2, in the way that, for T1 and classes whose constructor takes one int argument, it will compile with a default implementation, whereas for T2 and classes whose constructors are different, it will compile with a virtual function and enforce subclass to implement it with override. Somewhat like below:
Template <typename T>
class Foo {
// ingore all other functions
/* if T's signature takes one int input only
* compile like the below
*/
virtual std::shared_ptr<T> do_something(int x) {
return std::make_shared<T>(x);
}
/* if T's signature is different
* compile like the below and let the subclass implement it
*/
virtual std::shared_ptr<T> do_something(int x) = 0;
}
Is this possible without using any 3rd-party library? It's acceptable if we have to use macros/preprocessors.
It is also acceptable if there must be one function do_something there but for the case of mismatched signature, it just raises runtime exception, something like:
Template <typename T>
class Foo {
// ingore all other functions
virtual std::shared_ptr<T> do_something(int x) {
/* if T's signature takes one int input only
* compile like the below
*/
// return std::make_shared<T>(x);
/* if T's signature is different
* compile like the below and let the subclass implement it
*/
// std::throws...
}
}
As far as I can tell, we need class template specialization here. Not even C++20 requires-clauses can be applied to virtual functions, so the only thing we can do is have the whole class change.
template<typename T> // using C++20 right now to avoid SFINAE madness
struct Foo {
virtual ~Foo() = default;
virtual std::shared_ptr<T> do_something(int a) = 0;
};
template<std::constructible_from<int> T>
struct Foo<T> {
virtual ~Foo() = default; // to demonstrate the issue of having to duplicate the rest of the class
virtual std::shared_ptr<T> do_something(int a) {
return std::make_shared<T>(a);
}
};
If there is a lot of stuff in Foo, you can avoid duplicating it with a heap of upfront cost by moving do_something to its own class.
namespace detail { // this class should not be touched by users
template<typename T>
struct FooDoSomething {
virtual ~FooDoSomething() = default;
virtual std::shared_ptr<T> do_something(int a) = 0;
};
template<std::constructible_from<int> T>
struct FooDoSomething<T> {
virtual ~FooDoSomething() = default;
virtual std::shared_ptr<T> do_something(int a);
};
}
template<typename T>
struct Foo : detail::FooDoSomething<T> {
// other stuff, not duplicated
// just an example
virtual int foo(int a) = 0;
};
namespace detail {
template<std::constructible_from<int> T>
std::shared_ptr<T> FooDoSomething<T>::do_something(int a) {
Foo<T> &thiz = *static_cast<Foo<T>*>(this); // if you need "Foo things" in this default implementation, then FooDoSomething is *definitely* unsafe to expose to users!
return std::make_shared<T>(thiz.foo(a));
}
}
Godbolt
To convert this down from C++20, replace the concept-based specialization with old-type branching:
// e.g. for the simple solution
template<typename T, bool = std::is_constructible_v<T, int>>
struct Foo { // false case
// etc.
};
template<typename T>
struct Foo<T, true> { // true case
// etc.
};
// or do this to FooDoSomething if you choose to use that
A runtime error is much easier, at least in C++17 and up, since you can just use a if constexpr to avoid compiling the problematic code
template<typename T>
struct Foo {
virtual ~Foo() = default;
virtual std::shared_ptr<T> do_something(int a) {
if constexpr(std::is_constructible_v<T, int>) return std::make_shared<T>(a);
else throw std::logic_error("unimplemented Foo<T>::do_something");
}
};
I want to create a generic class containing a method displaying one message if the type of the class is int and the other when it's double. Here's my code:
template<class T>
class A {
public:
template <T> void B();
};
template<class T>
void A<int>::B{
//some code here
}
template<class T>
void A<double>::B{
//some code here
}
I got the following errors:
'double': illegal type for non-type template parameter '__formal'
'A<int>::B': unable to match function definition to an existing declaration
Thanks in advance for any solutions.
A couple of things:
There's no reason for B to be a template. You want to specialize for A
B is a method. Methods accept parameters. When defining the method, you omitted the parenthesis ()
Template specialization always involves an empty template parameter <>
Code:
template<class T>
class A {
public:
void B();
};
template<>
void A<int>::B(){
std::cout << "A<int>::B" << std::endl;
}
template<>
void A<double>::B(){
std::cout << "A<double>::B" << std::endl;
}
Demo
If you feel compelled to make B a template, I should note that in general one does not perform template specialization on functions. This is primarily because they cannot be partially specialized, and it's almost always better to write an overload. In your case, B takes no arguments, so there's some argument to be made in favor of specialization.
More often than not, one would use a tag dispatching approach instead, coupled with a helper function so that they can choose their desired function by taking advantage of overloading instead. Here's a simple example of tag dispatching for your case:
template<class T>
class A {
public:
template<class U>
void B()
{
B(ATag<U>{});
}
private:
template<class U>
struct ATag{};
void B(ATag<int>)
{
std::cout << "B<int>" << std::endl;
}
void B(ATag<double>)
{
std::cout << "B<double>" << std::endl;
}
};
tag dispatch demo
Is there a concise way to point to all instances of a templated function without using macros?
I have several templated functions that I want to test across a variety of types:
template<typename T>
void function1() {
return;
}
template<typename T>
void function2() {
return;
}
template<typename T>
void function3() {
return;
}
I can do this with a macro:
#define TEST_ACROSS_TYPES(fn) \
fn<int>(); \
fn<bool>(); \
fn<char>(); \
fn<double>(); \
TEST_ACROSS_TYPES(function1);
TEST_ACROSS_TYPES(function2);
But, (1) Macros are ugly and hard for others to follow, and (2) I'm using CATCH, which doesn't play nice when using macros to set up test cases.
Is there a way to do something like this:
void testAcrossTypes(SomeType f) {
f<int> ();
f<bool> ();
f<char> ();
f<double> ();
}
which seems much cleaner, except for the problem of defining SomeType. This question (How to define typedef of function pointer which has template arguments) explains how to define a pointer to a templated function; but, requires that the template arguments be specified.
For clarification: Imagine function1, function2, and function3 each test a different templated function. Each function needs to be tested for int, byte, char, double, etc. I want to avoid having to explicitly set up many (i.e. num_functions * num_types) tests for each function. Instead, I want to have a single method that points to the test function (function1, function2, etc.) and runs it for each template type, thus consolidating
function1<int>();
function1<byte>();
function1<char>();
function1<double();
...
function2<int>();
function2<byte>();
function2<char>();
function2<double();
...
function3<int>();
function3<byte>();
function3<char>();
function3<double();
...
into just one call per test function
testAcrossTypes(function1);
testAcrossTypes(function2);
testAcrossTypes(function3);
What you are trying to accomplish with
void testAcrossTypes(SomeType f) {
f<int> ();
f<bool> ();
f<char> ();
f<double> ();
}
would be possible if SomeType could be a template template argument. However, the standard does not allow function templates as template template argument.
From the C++11 Standard:
14.3.3 Template template arguments
1 A template-argument for a template template-parameter shall be the name of a class template or an alias template, expressed as id-expression.
Your best option is to use functors instead of functions. Example:
template<typename T>
struct function1
{
void operator()() {
return;
}
};
template<typename T>
struct function2
{
void operator()() {
return;
}
};
template < template <typename> class F>
void testAcrossTypes() {
F<int>()();
F<bool>()();
F<char>()();
F<double>()();
}
int main()
{
testAcrossTypes<function1>();
testAcrossTypes<function2>();
}
You can accomplish it by means of a type-erased functor, like the one in the following example:
#include<vector>
template<typename T>
void function1() { }
template<typename T>
void function2() { }
template<typename T>
void function3() { }
struct Test {
template<typename T>
static void proto() {
function1<T>();
function2<T>();
function3<T>();
}
void operator()() {
for(auto &f: vec) f();
}
template<typename... T>
static Test create() {
Test test;
int arr[] = { (test.vec.emplace_back(&proto<T>), 0)... };
(void)arr;
return test;
}
using func = void(*)(void);
std::vector<func> vec;
};
void testAcrossTypes(Test test) {
test();
}
int main() {
testAcrossTypes(Test::create<int, bool, char, double>());
}
It's easy to modify in both cases:
New functions require to be added to the proto static member method and that's all
Adding a new type is a matter of using it when call create, as shown in the above example
The functor will keep in charge of creating the N*M calls to be executed.
Moreover, you don't need to move your functions in a bunch of structs to be able to use them.
I'd like to call a function AsJson in a unified way irrespective of whether I'm dealing with an instance or a primitive type, or anything else.
I thought I could define an abstract base class which classes could inherit, and then define a template function that calls the AsJson member function when the template is specialized to appropriate classes. For other types, they could just specialize the template function.
Something like this:
#include <iostream>
class IInstrumented {
public:
virtual void AsJson() const = 0;
};
template <typename T>
void AsJson(const T&);
template <typename T>
void AsJson(const IInstrumented& instrumented) {
instrumented.AsJson();
}
class Foo : public IInstrumented {
public:
void AsJson() const override { std::cout << "A Foo!" << std::endl; }
};
template <>
void AsJson(const int& x) {
std::cout << "An integer!" << std::endl;
}
int main() {
Foo foo;
AsJson(foo);
int x = 3;
AsJson(x);
}
Unfortunately, this results in the following linker error:
special.cpp:(.text+0x56): undefined reference to `void AsJson<Foo>(Foo const&)'
Is this approach workable as is? Is the fix something relatively minor, or is an entirely different approach warranted?
Although there is an accepted answer, I thought I would point out why you had the error in the first place.
When compiling
AsJson(foo);
the compiler is looking for this template instantiation:
template <>
void AsJson(const Foo&);
which is not defined(undefined reference error) because
template <typename T>
void AsJson(const T&);
has no implementation. It is not using this template specialization:
template <>
void AsJson(const IInstrumented& instrumented) {
instrumented.AsJson();
}
because it is not the best candidate while deducting type. By changing the code to:
AsJson<IInstrumented>(foo);
You then explicitly tell the compiler which template specialization you want to use. Previous answer assume all types will inherits from IInstrumented which might not be the case hence why the static_assert checking for type was added.
By providing a default implementation, you can avoid the necessity to inherits from IInstrumentable. Adding an assert in there might help catching serializing unknown types.
You should change
template <typename T>
void AsJson(const IInstrumented& instrumented) {
instrumented.AsJson();
}
to
template <typename T>
void AsJson(const T& instrumented) {
instrumented.AsJson();
}
And then:
$ ./bla
A Foo!
An integer!
Now if you want to ensure that T is derived from IInstrumented. You should add (c++11 only) :
template <typename T>
void AsJson(const T& instrumented) {
static_assert(std::is_base_of<IInstrumented, T>::value,
"T must be a descendant of IInstrumented"
);
instrumented.AsJson();
}
Then with
class Bar {
public:
void AsJson() { std::cout << "A Foo!" << std::endl; }
};
The following code:
Bar bar;
AsJson(bar);
raises:
bla.cpp: In instantiation of ‘void AsJson(const T&) [with T = Bar]’:
bla.cpp:38:13: required from here
bla.cpp:13:3: error: static assertion failed: T must be a descendant of IInstrumented
static_assert(std::is_base_of<IInstrumented, T>::value,
See also How to ensure that the template parameter is a subtype of a desired type?
I have a template class, say:
template<class T>
class someClient
{
void someCallbackA() {foo_->onA();}
void someCallbackB() {foo_->onB();}
private:
T* foo_;
};
which I can instantiate with a bunch of different types T which support the onA and onB interface. I happen to have a case where two out of the several different types T I use needs a particular behavior controlled from someClient so I need to add some function doBar() to these two types (call them Edge1 and Edge2). Then I want a part of the someClient code to call foo_->doBar() but without breaking when the type of foo_ does not have that. Is there a way to use boost::enable_if to have a someClient::doBar() which will call foo_->doBar() only for those two types, but not be there, or expand to nothing if the types are not Edge1 or Edge2?
I was thinking along the lines of:
template <class T, enable_if<mpl_or<is_same<T,Edge1>, is_same<T,Edge2> > >
someClient<T>::doBar() {foo_->doBar();}
You don't need to pull any special tricks at all if you just don't call member functions that don't make sense. Template member functions are only specialized when needed (unless you add an explicit specialization). So the following code works fine:
template <typename T> struct Foo
{
void do_foo() { p->foo(); }
void do_bar() { p->bar(); }
T * p;
};
struct A { void foo() {} };
int main()
{
A a;
Foo<A> x = { &a };
x.do_foo();
}
The fact that Foo<A>::do_bar wouldn't compile is not an issue, since the member function is never instantiated. And p->bar isn't a compiler error, because p has a dependent type and the line is thus only parsed in the second lookup phase (which never happens).
I think this does what you want. I used C++11 <type_traits> instead of boost's:
struct Edge {
void doBar() { std::cout << "did Bar."; }
};
template<typename T>
class someClient
{
public:
template<typename U = T>
typename
std::enable_if<std::is_same<U, Edge>::value, void>::type
doBar() { foo_->doBar(); }
template<typename U = T>
void doBar( typename std::enable_if<!std::is_same<U, Edge>::value, void>::type* = 0 )
{ /* do nothing */ }
private:
T* foo_;
};
int main()
{
someClient<int> i;
someClient<Edge> e;
i.doBar();
e.doBar(); // outputs "did Bar."
}
doBar() needs to be template itself for this to work, explanation here: std::enable_if to conditionally compile a member function