I would like to define custom constants which can be used for an arbitrary type (e.g. float, double, etc.). As an example, suppose I wish to define a constant whose value is pi.
The obvious solution is to use #define pi 3.14159265359, but then pi would not be in a namespace and I risk a name collision. I'm not using C++14 so I can't use a variable template. The best way I can think to do this is like the following:
#include <iostream>
using namespace std;
namespace constants {
template<typename T> T pi() {
return 3.14159265359;
}
}
int main() {
float pitest = 0;
pitest = constants::pi<float>();
cout << pitest << endl;
cout << constants::pi<long double>() << endl;
cout << constants::pi<int>() << endl;
return 0;
}
I can now define these constants within a namespace and I can use an arbitrary (numerical) type as desired. However, there are at least two undesirable features of this:
It requires a function call which should not be necessary (it's just a constant!).
I have to specify the type in the function call, even if the function is returning to a variable of known type. For example, in the above code I have to use pitest = constants::pi<float>(); rather than simply pitest = constants::pi(); even though pitest is obviously a float.
Is there a better way to do this?
Why not use a special object with automatic conversion to any type?
static struct {
template<class T> operator T() const constexpr
{ return (T)3.14159265359; }
} pi;
You might even add specializations for bigger types, arbitrary-precision-arithmetic, formula-systems, whatever.
static struct { template<class T> operator T() const constexpr { return 3.14; } } pi;
is the first step.
template<class T> struct type {};
template<class T> constexpr T get_pi( type<T> ) { return 3.14; }
static struct { template<class T> operator T() const constexpr { return get_pi( type<T>{} ); } } pi;
is the second type -- now you can add new overloads for new types without having to specialize. All pi does is do the magic casting.
Sadly, this requires that we match the type exactly -- a new overload for int won't solve long, or a new overload for double won't solve float.
But this is C++, we can do it!
template<class T> struct contra_type {
constexpr contra_type(contra_type&&) {};
template<class U, class=typename std::enable_if< std::is_convertible< T, U >::value >::type>
constexpr contra_type( contra_type<U>&& ) {}
};
template<class T> constexpr auto get_pi( type<T>, ... )->decltype(T(3.14)) { return T(3.14); }
static struct { template<class T> operator T() const constexpr { return get_pi( contra_type<T>{} ); } } pi;
is the next step. Now we can add overloads for get_pi( type<bignum> ) and such and have it work. In fact, anything implicitly convertable from bignum will call get_pi( type<bignum> ) automatically.
Not sure how to enable ADL -- if I take a T*, I'll get covariant overloads not contravariant overloads (and as we are actually overloading on return type, that isn't want I want).
contra_type<U> is convertible to contra_type<T> if and only if T is convertable to U. This means that pi_func( contra_type<Foo>{} ) will attempt to find a pi_func that takes a type that can convert to Foo, and call that instead.
The ... overload gives us a default implementation that matches everything exactly, but because it has ... it will be preferred to call any other function instead of it that matches.
Related
In Python, you can return a class instead of its instance from a function, because a class is just an instance of a 'metaclass' called type.
Can you do the same in C++? Say I want a function to return a class, so that the calling code can then take the returned class and call a static method on it without ever creating an instance of it. Possible ?
You can't return a type; but you can return an identity object for a type. Then you can process it similarly to the given type. For example:
#include <iostream>
// in C++20, this is available as std::type_identity<>
template<typename T>
struct ID {};
template<typename T>
struct ToType;
template<typename T>
struct ToType<ID<T>>
{ using type = T; };
template<typename T>
using totype_t = typename ToType<T>::type;
struct MyType
{
static fn() { std::cout << "ok" << std::endl; }
};
auto GetMyType()
{
return ID<MyType>();
}
int main()
{
auto proc = [](auto x) {
totype_t<decltype(x)>::fn();
};
proc(GetMyType());
}
Note that, a function's return type must still be a given type, so you can't return this way based on runtime parameters. (You can, of course, use compile-time arguments for that.)
However, in C++ you also have type aliases. That's basically a way to write 'function-like' (mathematically: mapping) type expressions to define your type calculation. E.g.,
template<size_t I>
using MyType = std::conditional_t<(I < 50), char, int>; // if (I < 50) then char else int
A useful trick here is to use decltype(expression) for expression's evaluation type.
I am writing a wrapper class for callable types (pointer to function, functors, etc). I want to implement something like std::function.
I define constructor from pointer to function:
template <typename Ret, typename... Args>
class function<Ret(Args...)>
{
public:
function(Ret (func)(Args...))
{
m_fn_ptr = func;
}
}
Now, let's assume that i want to use my class like this:
int int_function(int n)
{
return n;
}
function<int(short)> wrapper(&int_function); // compile error
Despite that short are implicit convertable to int compiler cannot deduce template parameters and call appropriate constructor.
Then i tried this:
template <typename FRet, typename... FArgs>
function(FRet (func)(FArgs...))
{
m_fn_ptr = static_cast<Ret (*f)(Args...)>(func);
}
But I got invalid static cast.
How can I fix that ?
The super_func is a function object with no state that can convert to any compatible call signature.
template<class T>using type=T;
template<class Sig, Sig* func>
struct super_func;
template<class R, class...Args, R(*func)(Args...)>
struct super_func<R(Args...), func> {
using Sig = R(Args...);
using pSig = Sig*;
template<class R2, class...Args2, std::enable_if_t<
std::is_convertible<
std::result_of_t<pSig(Args2...)>,
R2
>{}
&& !std::is_same<R2, void>{},
bool
> =true>
constexpr operator type<R2(Args2...)>*() const {
return [](Args2...args)->R2{
return func(std::forward<Args2>(args)...);
};
}
template<class...Args2, std::enable_if_t<
std::is_same<
std::result_of_t<pSig(Args2...)>,
R
>{},
bool
> =true>
constexpr operator type<void(Args2...)>*() const {
return [](Args2...args)->void{
func(std::forward<Args2>(args)...);
};
}
constexpr operator pSig() const {
return func;
}
constexpr R operator()(Args...args)const{
return func(std::forward<Args>(args)...);
}
};
live example. A super_func is stateless. To use it on a function foo, do:
super_func< decltype(foo), &foo > super_foo;
and you get a callable stateless empty object which behaves a lot like foo does, except you can assign it to a pointer to any compatible function pointer and it generates it "on the fly" at compile time.
A super_foo can be fed to your function object.
Doing this on the fly doesn't work without the exterior help, as we need the foo to be a truly static bit of information. By the time it becomes a variable, it is too late to do this statelessly, so we cannot use the lambda trick (without an extra pvoid) to generate a function pointer for the exact signature we want.
You could do a macro:
#define SUPER(X) super_func< decltype(X), &X >{}
and then create your function object with function<double()> f(SUPER(foo));
Another approach is to store an extra pointer's worth of state, and create "the fastest possible delegate" style type erasure. (that term can be googled for one of many implementations, each faster than the last).
How can I fix that ?
Use the correct types when creating wrapper.
Instead of using
function<int(short)> wrapper(&int_function);
use
function<int(int)> wrapper(&int_function);
Remember that class templates instantiated with int and short are very different types and are not convertible to each other.
template <typename T> struct Foo {};
Foo<int> a;
Foo<short> b = a; // Not OK.
Foo<short> c;
Foo<int> d = c; // Not OK.
Your function constructor expects a pointer to a function that takes a short, not an int. The fix is to provide it such a function. The easiest way to do that is to use a lambda with an empty capture-list, that is implicitly convertible to a function pointer:
function<int(short)> wrapper( [](short s) { return int_function(s); } );
I am trying to achieve type checking of template class parameters by disallowing implicit type conversions such as string->bool thereby throwing compile error.
The specific scenario is a simple one as follows:
#include <iostream>
#include <string>
using namespace std;
template <class T>
class myPair {
T a, b;
public:
myPair(T first, T second ) {
a = first;
b = second;
}
void test();
};
typedef myPair<bool> boolParm;
template<class T>
void myPair<T>::test() {
if(a == true) {
cout << "a is true" << endl;
} else {
cout << "a is false" << endl;
}
if(b == true) {
cout << "b is true" << endl;
} else {
cout << "b is false" << endl;
}
}
int main() {
boolParm myObj(false, "false");
myObj.test();
return 0;
}
The output of the above scenario is undesirable since the user may inadvertently pass 2 different types: bool and string and receive the first one as false (correct since passed as bool) but the second one will be true (incorrect since implicit type conversion from string to bool).
I wish to restrict the user code in main() to throw compile errors and disallowing string/int parameters to pass in the constructor. It should only allow bool.
I tried by using an overloaded constructor myPair(bool first, string second) but it didn't match since I guess the implicit type conversion from string->bool happens before the constructor is called.
Is there any solution using template specializations in this scenario?
Any help is highly appreciated
Thanks
One workaround is to add a templated factory function to create the myPair.
template <typename T>
myPair<T> makeParam(T a, T b) {
return myPair<T>(a, b);
}
That will fail to compile with ambiguous template parameter T if the types don't match. You can extend this with template specializations explicitly forbidding certain types for T. Your main function will then look something like:
int main() {
boolParm myObj = makeParam(false, "false");
myObj.test();
return 0;
}
Alternatively change the constructor:
template <typename U, typename V>
myPair(U a, V b);
And specialize as necessary
An example of such specialization:
template <class T>
class myPair {
T a, b;
public:
template <typename U, typename V> // generic version
myPair(U first, V second)
{
// intentionally fail to compile
static_assert(false, "don't support generic types");
}
template <> // template specialization
myPair(T first, T second)
{
// explicitly require exactly type T
a = first;
b = second;
}
};
It is indeed weird behavior at first glance; but as far as I can say, you can't prohibit that - not for primitive types like bool, anyway.
The implicit conversion of parameters happen before you get a say on it, and it seems there is an implicit primitive type conversion from char const * to bool.
See also e.g. this other question: Why does a quoted string match bool method signature before a std::string?
My example below suggests that implicit conversions from non-template types to template types won't work as seamlessly as those only involving non-template types. Is there a way to make them work nonetheless?
Example:
struct point;
template<unsigned d> struct vec {
vec() { }
// ...
};
template<> struct vec<2> {
vec() { }
vec(const point& p) { /* ... */ } // Conversion constructor
// ...
};
struct point {
operator vec<2>() { return vec<2>(/* ... */); } // Conversion operator
};
template<unsigned d> vec<d> foo(vec<d> a, vec<d> b) {
return vec<d>(/* ... */);
}
template<unsigned d1, unsigned d2>
vec<d1 + d2> bar(vec<d1> a, vec<d2> b) {
return vec<d1 + d2>(/* ... */);
}
int main(int argc, char** argv) {
point p1, p2;
vec<2> v2;
vec<3> v3;
foo(v2, p1);
foo(p2, v2);
foo(p1, p2);
bar(v3, p1);
}
Is there a way to let this code auto-convert from point to vec<2>?
I know I can overload foo and bar to allow for point arguments, delegating to the vec implementation using an explicit conversion. But doing this for all parameter combinations will become tedious, particularly for functions with many such parameters. So I'm not interested in solutions where I have to duplicate code for every parameter combination of every function.
It appears that neither the conversion constructor nor the cast operator are sufficient to achieve this. At least my gcc 4.7.1 reports no matching function call, although it does name the desired function in a notice, stating that ‘point’ is not derived from ‘vec<d>’.
There is no direct way to get the conversion from point to vec<2>, because at the time when the function call foo(v1,p1) is processed, a function foo that expects a vec<2> as second argument does not exist yet. It's just a function template, and in order for this to be instantiated to a foo(const vec<2> &,const vec<2> &), a function call with these exact argument types would have to be given.
In order for the code to work, the compiler would have to guess both how to instantiate the template parameters, and what type the point argument to convert to. This is too much in the general case (although in your particular code it appears simple, because there is no other possible way to interpret the intent of the programmer).
In terms of solving this, the only thing I can think of is to create highly templated conversion functions:
template <typename T>
struct make_vec
{ };
template <unsigned d>
struct make_vec<vec<d>>
{
static constexpr unsigned dim = d;
using type = vec<dim>;
static const type &from(const type &v)
{ return v; }
};
template <>
struct make_vec<point>
{
static constexpr unsigned dim = 2;
using type = vec<dim>;
static type from(const point &p)
{ return type(p); }
};
template <typename T>
typename make_vec<typename std::decay<T>::type>::type make_vec_from(T&& arg)
{ return make_vec<typename std::decay<T>::type>::from(std::forward<T>(arg)); }
And then implement the foo and bar functions as general templates (accepting all kinds of types, not only vec<d>, using make_vec defined above to convert the given types to the right kind of vec<d>):
namespace detail {
/* Your original implementation of foo. */
template<unsigned d> vec<d> foo(vec<d>, vec<d>) {
return vec<d>(/* ... */);
}
}
/* Templated version of foo that calls the conversion functions (which do
nothing if the argument is already a vec<d>), and then calls the
foo() function defined above. */
template <typename T, typename... Ts>
typename make_vec<typename std::decay<T>::type>::type foo(T&& arg, Ts&&... args)
{ return detail::foo(make_vec_from(arg),make_vec_from(args)...); }
In the case of bar you also need a way to calculate the return type, which is vec<d1+d2+d3...>. For this, a sum calculator is required, also templated:
template <typename... Ts>
struct dsum {
static constexpr unsigned value = 0;
};
template <typename T, typename... Ts>
struct dsum<T,Ts...> {
static constexpr unsigned value = make_vec<typename std::decay<T>::type>::dim + dsum<Ts...>::value;
};
Then, the return type of bar() is vec<dsum<T,Ts...>::value>.
A fully working example is here: http://liveworkspace.org/code/nZJYu$11
Not exactly simple, but might be worth it if you really have extremely many different combinations of arguments.
int x = fromString("test") :could not deduce template argument for 'ValueType'
int x = fromString<int>("test") : works fine as expected
So why does the compiler struggle here? I see it with all kinds of real template functions, not just this silly example. It must be a feature of the language, but what?
You can't deduce based on the return type. You can, however, implement a workaround with similar syntax, using the overloaded cast operator:
#include <iostream>
#include <sstream>
#include <string>
using namespace std;
class FromString{
private:
string m_data;
public:
FromString(const char*data) : m_data(data) {}
template<typename T>
operator T(){
T t;
stringstream ss(m_data);
ss >> t;
return t;
}
};
template<> FromString::operator bool(){
return (m_data!="false"); //stupid example
}
int main(){
int ans = FromString("42");
bool t = FromString("true");
bool f = FromString("false");
cout << ans << " " << t << " " << f << endl;
return 0;
}
Output:
42 1 0
C++ doesn't do type inference on the return value. I.e., the fact that it is being assigned to an int isn't used in template parameter deduction.
(Removed edit, since someone else presented the overloaded cast solution already.)
Besides the bad choice for an example (probably makes sense to have int x = to<int>("1235") rather than toString), the problem is that the return type does not participate in overload resolution or type inference[1]. The reason for this is that the expression can be used in many places where the type of the return cannot be deduced:
// assuming template <typename T> T to( std::string ):
//
f( to("123") ); // where there are two overloads f(int), f(double)
int x = 1.5 * to("123"); // T == int? T == double?
to("123"); // now what? returned object can be ignored!
So the decision is that the return type will not take part in overload resolution or type deduction.
[1] There is a single exception to this rule, which is the evaluation of a function pointer with more than one overload, where the overload must be selected by either the destination pointer or an explicit cast, but this is just the one exception and is not used in any other context:
void f();
void f(int);
void g( void (*)() );
void g( void (*)(int) );
void (*p1)() = &f; // overload selected based on destination type
void (*p2)(int) = &f;
g( (void (*)(int))&f ); // overload selected based on explicit cast
It looks like your template has the return type templated which cannot be automatically deduced which is why you need to add it in here.
The return type of a function is dependent on overload resolution, not the other way around.
There is a trick that works though: operator= usually exists only for equal LHS/RHS argument types, except when an explicit operator= is defined (whether as standalone or as a member does not matter).
Thus, overload resolution will find operator=(int &, int), and see if the return value from your function is convertible to int. If you return a temporary that has an operator int, this is an acceptable resolution (even if the operator int is in the generic form of a template<typename T> operator T).
Thus:
template<typename T, typename U>
U convert_impl(T const &t);
template<typename T>
struct convert_result {
convert_result(T const &t) : t(t) { }
template<typename U> operator U(void) const { return convert_impl<U>(t); }
T const &t;
};
template<typename T>
convert_result<T> convert(T const &t) { return t; }