Constraint API template types neatly - c++

I have a template API function in my class called template<typename T> T Get(/*stuff*/);. My source file implements this function for a certain list of T types. If the user wants to use a type that I have not implemented, then I want the result to be a compile error, and not a linker error. I don't care much about the compile message yet. Here's what I've got so far:
MyClass.h
#pragma once
#define API_TYPE(X) \
template<> struct Implemented<X> : public API<X> {}
namespace MyClassAPI
{
template<typename T> struct API
{
static T Get(const T&);
};
template<typename T> struct Implemented {};
API_TYPE(bool);
}
class MyClass
{
template<typename T> friend struct MyClassAPI::API;
public:
template<typename T> T Get(const T& t) const
{
return MyClassAPI::Implemented<T>::Get(t);
}
};
MyClass.cpp
#include "MyClass.h"
namespace MyClassAPI
{
template<typename T> T API<T>::Get(const T& t) { return t; }
//template struct API<bool> //Why do I need this?
}
main.cpp
#include "MyClass.h"
#include <iostream>
#include <cassert>
using namespace std;
// Main File
int main() {
MyClass c;
cout << "Getting true: " << c.Get(true) << endl;
return 0;
}
So my question is about a line in MyClass.cpp. Why do I need to replicate the API<bool> explicit declaration in the source file with template struct API<bool>;? Shouldn't it know to expand the template function definition from the header file's declaration when Implemented<bool> : public API<bool> inherits from it?
Also, is there a way to do this without declaring my accepted type list twice?
Error without the line:
g++ -Wfatal-errors -Werror -std=c++11 -g -O0 -Wall -c MyClass.cpp -o MyClass.o
g++ -Wfatal-errors -Werror -std=c++11 -g -O0 -Wall test.cpp MyClass.o -o test
/tmp/ccVxp4F3.o: In function `bool MyClass::Get<bool>(bool const&) const':
MyClass.h:25: undefined reference to `MyClassAPI::API<bool>::Get(bool const&)'
collect2: error: ld returned 1 exit status
make: *** [test] Error 1

The problem with your template is you're defining it's members in a separate translation unit, hence they are not visible to main.cpp, and C++ does not support separate translation of templates.
When you use template struct API<bool>; you ask the compiler to explicitly instantiate API<T> for T = bool. However, when doing so, you should also let the other translations units know that the instantiation takes place elsewhere by having a similar directive in the header file with template declaration:
extern template struct API<bool>;
Otherwise, move the definition of Get to the header file or include .cpp file in the header file (not recommended) and rely on the compiler to instantiate template members for you.
As about limiting the list of viable template parameters, I suggest an approach based on template metaprogramming.
First we'll define some facility for checking whether a type belongs to a list of types, let's call it is_in<DesiredType, ViableTypes...>. It will accept as template parameters a type T to look for and a list of types InTypes... for search, and provide result by a static boolean member available at compile-time.
It is implemented as a simple recursive algorithm on a list, checking each element of the list against DesiredType until DesiredType is found or the end of list is met:
#include <type_traits>
template<typename...> struct is_in;
template<typename T, typename InType, typename... InTypes> struct is_in<T, InType, InTypes...> {
static constexpr bool value = std::is_same_t<T, InType> ||
is_in<T, InTypes...>::value;
}
template<typename T> struct is_in<T> {
static constexpr bool value = false;
}
Now, having is_in, we can just use static_assert and explicitly specify viable types for a template:
template<typename T> struct API
{
// Only allow API<bool> and API<int>
static_assert(is_in<T, bool, int>::value, "invalid template type for API<T>");
static T Get(const T&);
};

You almost have it there.
You need update MyClass.h to provide explicit instantiations of couple of functions and implement them in MyClass.cpp.
In the .h file, add:
// Explicit instantiations
namespace MyClassAPI
{
template<> int API<int>::Get(const int&);
template<> double API<double>::Get(const double&);
}
In the .cpp file, add:
// Implement the explicit instantiations
namespace MyClassAPI
{
template<> int API<int>::Get(const int& in)
{
// Add whatever logic that makes sense for this type.
return 2*in;
}
template<> double API<double>::Get(const double& in)
{
// Add whatever logic that makes sense for this type.
return 10*in;
}
}
Here's a single file version of working code:
#define API_TYPE(X) \
template<> struct Implemented<X> : public API<X> {}
namespace MyClassAPI
{
template<typename T> struct Implemented;
template<typename T> struct API
{
static T Get(T const&);
};
API_TYPE(int);
API_TYPE(double);
}
class MyClass
{
template<typename T> friend struct MyClassAPI::API;
public:
template<typename T> T Get(const T& t) const
{
return MyClassAPI::Implemented<T>::Get(t);
}
};
// Explicit instantiations
namespace MyClassAPI
{
template<> int API<int>::Get(const int&);
template<> double API<double>::Get(const double&);
}
#include <iostream>
int main()
{
MyClass a;
std::cout << a.Get<int>(10) << std::endl;
std::cout << a.Get<double>(10) << std::endl;
// Does not work. Produces compiler error.
// std::cout << a.Get<float>(10) << std::endl;
}
// Implement the explicit instantiations
namespace MyClassAPI
{
template<> int API<int>::Get(const int& in)
{
return 2*in;
}
template<> double API<double>::Get(const double& in)
{
return 10*in;
}
}
Output:
20
100
Update
Here'a multiple file version:
MyClass.h:
#pragma once
#define API_TYPE(X) \
template<> struct Implemented<X> : public API<X> {}
namespace MyClassAPI
{
template<typename T> struct Implemented;
template<typename T> struct API
{
static T Get(T const&);
};
API_TYPE(int);
API_TYPE(double);
}
class MyClass
{
template<typename T> friend struct MyClassAPI::API;
public:
template<typename T> T Get(const T& t) const
{
return MyClassAPI::Implemented<T>::Get(t);
}
};
// Explicit instantiations
namespace MyClassAPI
{
template<> int API<int>::Get(const int&);
template<> double API<double>::Get(const double&);
}
MyClass.cc:
#include "MyClass.h"
// Implement the explicit instantiations
namespace MyClassAPI
{
template<> int API<int>::Get(const int& in)
{
return 2*in;
}
template<> double API<double>::Get(const double& in)
{
return 10*in;
}
}
main.cc:
#include <iostream>
#include "MyClass.h"
int main()
{
MyClass a;
std::cout << a.Get<int>(10) << std::endl;
std::cout << a.Get<double>(10) << std::endl;
// Does not work.
// std::cout << a.Get<float>(10) << std::endl;
}
It also builds successfully and produces the same result.

Related

Two c++ programs with the same problem but one compiles and links, the other errs

Having two c++ programs. One is simple and the other is a little complex. But all have the same problem. The simple one has the following files.
foo.h:
class foo{
static const int array[3];
};
const int foo::array[3] = { 1, 2, 3 }; <-------- Here is the line causing the error.
foo.cc:
#include "foo.h"
main.cc:
#include "foo.h"
int main()
{
}
while compile and link with the following command:
clang++ -c *.cc -std=c++17
clang++ *.o -o a.out -std=c++17
it reports the following error:
main.o:(.rodata+0x0): multiple definition of `foo::array'
foo.o:(.rodata+0x0): first defined here
clang-14: error: linker command failed with exit code 1 (use -v to see invocation)
For the complex one, having the following files. t_a.h:
#pragma once
#include "t_b.h"
class TA : public TB,
public TIT<TB, TA> {
public:
static const char* name() { return "TA"; }
};
t_b.h:
#pragma once
#include "t_r.h"
class TB {
TI<TB> t_i() const { return t_i_; }
private:
template <typename T, typename U>
friend class TIT;
TI<TB> t_i_{TI<TB>::kUkT};
};
t_i.h:
#pragma once
#include <string>
#include <stdint.h>
template <typename BaseT>
class TR;
template <typename BaseT>
class TI {
public:
const std::string& name() const;
int8_t id() const { return id_; }
bool operator==(TI other) const { return id_ == other.id(); }
bool operator!=(TI other) const { return id_ != other.id(); }
static const TI kUkT;
private:
friend class TR<BaseT>;
explicit TI(int8_t id) : id_(id) {}
int8_t id_;
};
template <typename BaseT, typename DerivedT>
class TIT {
public:
static const TI<BaseT> kT;
TIT() {
static_cast<BaseT*>(static_cast<DerivedT*>(this))->t_i_ = kT;
}
static bool classof(const BaseT* obj) { return obj->t_i() == kT; }
};
template <typename BaseT>
TI<BaseT> RST(const std::string& t);
template <typename BaseT, typename DerivedT>
const TI<BaseT> TIT<BaseT, DerivedT>::kT =
RST<BaseT>(DerivedT::name()); <-------- This block of code should cause a similar error, but it does not.
t_r.h:
#pragma once
#include <cassert>
#include <map>
#include <mutex>
#include <string>
#include <vector>
#include "t_i.h"
template <typename BaseT>
class TR {
public:
TR(const TR&) = delete;
TR& operator=(const TR&) = delete;
static TR& GI();
TI<BaseT> RT(const std::string& t);
const std::string& GTN(TI<BaseT> i) const;
private:
TR() = default;
mutable std::mutex mutex_;
std::vector<std::string> names_;
std::map<std::string, int8_t> name_to_id_;
};
template <typename BaseT>
TR<BaseT>& TR<BaseT>::GI() {
static TR<BaseT> r;
return r;
}
template <typename BaseT>
TI<BaseT> TR<BaseT>::RT(const std::string& t) {
std::lock_guard<std::mutex> guard(mutex_);
assert(name_to_id_.find(t) == name_to_id_.end());
assert(names_.size() < static_cast<decltype(names_.size())>(
std::numeric_limits<int8_t>::max()));
int8_t id = static_cast<int8_t>(names_.size());
names_.emplace_back(t);
name_to_id_[t] = id;
return TI<BaseT>(id);
}
template <typename BaseT>
const std::string& TR<BaseT>::GTN(
TI<BaseT> info) const {
std::lock_guard<std::mutex> guard(mutex_);
int8_t id = info.id();
assert(id >= 0);
assert(static_cast<size_t>(id) < names_.size());
return names_[id];
}
template <typename BaseT>
TI<BaseT> RST(const std::string& type) {
return TR<BaseT>::GI().RT(type);
}
template <typename BaseT>
const std::string& TI<BaseT>::name() const {
return TR<BaseT>::GI().GTN(*this);
}
template <typename BaseT>
const TI<BaseT> TI<BaseT>::kUkT =
RST<BaseT>("Uk");
use_t_i_1.cc:
#include "t_a.h"
TIT<TB, TA> test_class_1;
use_t_i_2.cc:
#include "t_a.h"
int main() {
TIT<TB, TA> test_class_2;
}
When compile and link by the following command:
clang++ -c *.cc -std=c++17
clang++ *.o -o a.out -std=c++17
No error occurs.
What could be the cause why the same class of grammar mistake exists in both of the two programs while one reports error but the other one does not? Could anyone please explain this? Could anyone please does a small adjustment to the second complex program so it also errs with the same class of error? Thanks in advance.
this line
const int foo::array[3] = { 1, 2, 3 };
needs to be in one .cc file, not in a header. Just pick one
C++ explicitly allows multiple definitions of templated entities:
basic.def.odr#13:
There can be more than one definition of a
class type ([class]),
enumeration type ([dcl.enum]),
inline function or variable ([dcl.inline]),
templated entity ([temp.pre]),
default argument for a parameter (for a function in a given scope) ([dcl.fct.default]), or
default template argument ([temp.param])
So it does not caused the multiple definition error.

undefined reference on Friend template function

I am trying to implement a friend template function over a normal class.
fren.h
#include <iostream>
namespace sn{
class Fren{
private:
int x;
public:
Fren(int y):x(y){
}
template<typename B>
friend void getValue(B& asd);
};
template<typename B>
void getValue(B& asd);
}
fren.cpp
#include "fren.h"
namespace sn{
template<typename B>
void getValue(B& asd){
std::cout<<asd.x<<std::endl;
}
}
main.cpp
#include "fren.h"
int main() {
sn::Fren f(10);
sn::getValue(f);
return 0;
}
I am trying to get the private value x of Fren.
But i get the "undefined reference to" error.
First off, unless there is a solid reason for otherwise, most of this belongs in a single header file. See "Why can templates only be implemented in the header file?" for why.
That said, if you want to use explicit instantiation for your function template, you can. Note the following, which is also more restrictive than your friending. This code only friends the function templates with matching template arguments to Fren:
func.h
#ifndef SAMPLE_FUNC_H
#define SAMPLE_FUNC_H
namespace sn
{
template<class T>
struct Fren;
template<class T>
void getValue(const Fren<T>& s);
template<class T>
struct Fren
{
friend void getValue <>(const Fren<T>&);
Fren() : x(42) {}
private:
int x;
};
} // namespace
#endif
func.cpp
#include "func.h"
#include <iostream>
namespace sn
{
template<class T>
void getValue(const Fren<T>& s)
{
std::cout << s.x << ':' << __PRETTY_FUNCTION__ << '\n';
}
// explicit instantiation here
template void getValue <int> (const Fren<int>&);
}
main.cpp
#include <iostream>
#include <string>
#include "func.h"
int main()
{
sn::Fren<int> s;
sn::getValue(s);
}
Output
42:void sn::func(const S<T> &) [T = int]
Compiled with Apple LLVM version 6.1.0 (clang-602.0.53) (based on LLVM 3.6.0svn)
In short, your code appears to be missing the actual explicit instantiation.

Template classes using each other gives ambiguity error

I have two template classes A and B in the same header file as follows:
template <typename T>
class FirstClass {
public:
bool convert(const FirstClass<T>& f){...}
bool convert(const SecondClass<T>& s){...}
};
template <typename T>
class SecondClass {
public:
bool convert(const FirstClass<T>& f){...}
bool convert(const SecondClass<T>& s){...}
};
In order to resolve any unknown class errors, I tried adding a forward declaration:
template <typename T> class SecondClass ; //adding this to the beginning of the file
I get the following error:
2 overloads have similar conversions
could be 'bool FirstClass<T>::convert(const FirstClass<T>& )'
or
could be 'bool FirstClass<T>::convert(const SecondClass<T>& )'
while trying to match the argument list '(FirstClass<T>)'
note: qualification adjustment (const/volatile) may be causing the ambiguity
I am assuming this is because I am using a forward declared class. Other than moving the implementation to Cpp files( which I have been told is cumbersome), is there any other efficient solution to this?
I am using VisualStudio 2010 on Windows 7
Just put the forward declarations before you define any of the two classes.
#include <iostream>
template<typename> class FirstClass;
template<typename> class SecondClass;
template <typename T>
class FirstClass {
public:
bool convert(const FirstClass<T>& f) { std::cout << "f2f\n"; }
bool convert(const SecondClass<T>& s){ std::cout << "f2s\n"; }
};
template <typename T>
class SecondClass {
public:
bool convert(const FirstClass<T>& f){ std::cout << "s2f\n"; }
bool convert(const SecondClass<T>& s){ std::cout << "s2s\n"; }
};
int main()
{
FirstClass<int> f;
SecondClass<int> s;
f.convert(f);
f.convert(s);
s.convert(f);
s.convert(s);
}
Output on Ideone

explicit specialization template class member function with different return type

I am trying to port some C++ code from Windows to Solaris(Unix). There are some template code need to be changed. I am using Solaris' compiler CC, g++ should have same issue.
I have a particular part of code introduce some trouble. They are simplified as following:
#include <exception>
#include <cmath>
#include <string>
#include <iostream>
// define the "not implement" error
class tempException: public std::exception
{
public:
virtual const char* what() const throw()
{
return "not been implemented!";
}
} nondeferr;
// the template class
template <typename T>
class A
{
public:
template <typename Val>
Val getValue(T t) { throw nondeferr; }
template<>
double getValue(T t) { return exp( 1.5 * t ); } //Specialize the getValue for double type.
};
// test code
int main()
{
try
{
A<int> testA;
std::cout << testA.getValue<double>(2) << std::endl;
std::cout << testA.getValue<std::string>(2) << std::endl;
}
catch (tempException& e)
{
std::cout << e.what() << std::endl;
}
return 0;
}
To compile this sample code in UNIX, the compilation error comes out as the explicit specialization cannot be in the class A scope.
Here the getValue function only different from the return type, so we cannot modify it using the overload way.
And for some reason, change class A with simple template variable T to class A with double template variables T and Val is not allowed. It will introduce a lots of changes when we try to use this basic class.
May I know if there is any solution? I am currently remove the getValue function, replace it as getDoubleValue... But that is not so good too.
For those who interested, now the class A looks like this:
template <typename T>
class A
{
public:
// the Get Value we want
template <typename R>
R getValue(T t) { return get_value_impl<R>::apply(*this, t); }
// the general get value struct
template<typename R, typename = void>
struct get_value_impl
{
static R apply(A a, T t) { throw nondeferr; }
};
// partial specialization, which is allowed in std C++
template <typename S>
struct get_value_impl<double, S>
{
static double apply(A a, T t) { return exp( 1.5 * t ); }
};
};
The logic behind is explicit specialization is not allowed in standard. However, partial specialization is allowed. Thanks Anycorn again for the splendid solution.
// the template class
template <typename T>
class A {
template<>
double getValue(T t) { return exp( 1.5 * t ); }
};
This isnt allowed by standard.
do:
template <typename T>
class A {
template<class R>
R getValue(T t) { return get_value_impl<double>::apply(*this, t); }
template<class R, class = void>
struct get_value_impl; // specialize this
};
It is not allowed to specialize a member function without specializing the surrounding class. Visual Studio allows this as an extension.

explicit specialization of template class member function

I need to specialize template member function for some type (let's say double). It works fine while class X itself is not a template class, but when I make it template GCC starts giving compile-time errors.
#include <iostream>
#include <cmath>
template <class C> class X
{
public:
template <class T> void get_as();
};
template <class C>
void X<C>::get_as<double>()
{
}
int main()
{
X<int> x;
x.get_as();
}
here is the error message
source.cpp:11:27: error: template-id
'get_as<double>' in declaration of primary template
source.cpp:11:6: error: prototype for
'void X<C>::get_as()' does not match any in class 'X<C>'
source.cpp:7:35: error: candidate is:
template<class C> template<class T> void X::get_as()
What is the problem here, and how can I fix it?
It doesn't work that way. You would need to say the following, but it is not correct
template <class C> template<>
void X<C>::get_as<double>()
{
}
Explicitly specialized members need their surrounding class templates to be explicitly specialized as well. So you need to say the following, which would only specialize the member for X<int>.
template <> template<>
void X<int>::get_as<double>()
{
}
If you want to keep the surrounding template unspecialized, you have several choices. I prefer overloads
template <class C> class X
{
template<typename T> struct type { };
public:
template <class T> void get_as() {
get_as(type<T>());
}
private:
template<typename T> void get_as(type<T>) {
}
void get_as(type<double>) {
}
};
If one is able to used std::enable_if we could rely on SFINAE (substitution failure is not an error)
that would work like so (see LIVE):
#include <iostream>
#include <type_traits>
template <typename C> class X
{
public:
template <typename T,
std::enable_if_t<!std::is_same_v<double,T>, int> = 0>
void get_as() { std::cout << "get as T" << std::endl; }
template <typename T,
std::enable_if_t<std::is_same_v<double,T>, int> = 0>
void get_as() { std::cout << "get as double" << std::endl; }
};
int main() {
X<int> d;
d.get_as<double>();
return 0;
}
The ugly thing is that, with all these enable_if's only one specialization needs to be available for the compiler otherwise disambiguation error will arise. Thats why the default behaviour "get as T" needs also an enable if.
Probably the cleanest way to do this in C++17 and on-wards is to use a if constexpr in combination with the std::is_same_v type trait without explicitly specialisation at all:
#include <iostream>
#include <type_traits>
template <typename C>
class X {
public:
template <typename T>
void get_as() {
// Implementation part for all types
std::cout << "get as ";
// Implementation part for each type separately
if constexpr (std::is_same_v<double, T>) {
std::cout << "'double'";
} else if constexpr (std::is_same_v<int, T>) {
std::cout << "'int'";
} else {
std::cout << "(default)";
}
// Implementation part for all types
std::cout << std::endl;
return;
}
};
int main() {
X<int> d {};
d.get_as<double>(); // 'double'
d.get_as<int>(); // 'int'
d.get_as<float>(); // (default)
return EXIT_SUCCESS;
}
Try it here!
If you need to have a return type as well you could declare the return type as auto:
template <typename T>
auto get_as() {
if constexpr (std::is_same_v<double, T>) {
return 0.5;
} else {
return 0;
}
}