Ensuring type safety without variants - c++

Consider the following classes:
template <typename T>
class A {
public:
A(B<T> b) : b_(b) { }
T foo() {
return b_.foo();
}
private:
class B<T> b_;
}
template typename<T>
class B {
public:
T foo();
}
This nicely enforces typing across the stack (you can keep adding more layers and type them on a single type. However, I would like to have two different options on layer two:
template <typename T, typename Y>
class A {
public:
T foo() {
return b_.foo();
}
Y foo() {
return c_.foo();
}
private:
class B<T> b;
class C<Y> c;
}
template typename<T>
class B {
public:
T foo();
}
template typename<T>
class C {
public:
T foo();
}
Is there some way I could templetize the class with multiple typenames and achieve these scheme? Mind, in some situations T and Y might be the same, so there must be some more differentiations (for example B<T> vs C<Y>).
I believe I could have multiple functions foo1 and foo2 returning different values. However, I am looking for extensible solution where the customers can provide their own typenames and potentially more than two. Ideally, there would be a single overload (maybe taking identity of the internal class?)

Your idea is hard to understand, but I think you mean to take a list of template<typename> typenames and an equal number of typenames and construct the product of their zipped application?
// wrap a template<typename> typename into a normal type
// e.g. index_of_v<std::vector, std::vector> fails, but
// index_of_v<suspend<std::vector>, suspend<std::vector>> gives 0
template<template<typename> typename>
struct suspend { };
// search for X in Xs and produce a static constexpr std::size_t member values containing the index
// This is just the natural functional programming way to search a linked list
// indexOf(x, Nil) = undefined
// indexOf(x, Cons(y, xs)) = x == y ? 0 : 1 + indexOf(x, xs)
// deriving from std::integral_constant is really just noise;
// could be replaced with an explicit member definition
// but it's considered idiomatic to do this for some reason that I've forgotten
template<typename X, typename... Xs>
struct index_of { }; // base case, the default template only fires when Xs is empty, so the index is undefined
template<typename X, typename... Xs>
struct index_of<X, X, Xs...> : std::integral_constant<std::size_t, 0> { }; // if X is at the top of the list, it has index 0
template<typename X, typename Y, typename... Xs>
struct index_of<X, Y, Xs...> : std::integral_constant<std::size_t, index_of<X, Xs...>::value + 1> { }; // if it isn't, find it's index relative to the tail of the list, then shift it to be the index relative to the whole
// instead of writing index_of<X, Xs..>::value, write index_of_v<X, Xs...>
// this is a convention that you see in the standard library
template<typename X, typename... Xs>
inline constexpr std::size_t index_of_v = index_of<X, Xs...>::value;
// a class cannot have two lists of variadic template parameters
// the easiest thing to do is split the templating into two stages
// the outer template, choices, takes a list of templates as parameters
// template<typename T> class std::vector;
// int is a "base" type, so you can pass int to std::vector<int>
// but std::vector is not like int: std::vector<std::vector> is meaningless
// std::vector alone is just a template<typename> typename
// a function from types to types
template<template<typename> typename... Cs>
struct choices {
// 2nd "stage" takes the list of "normal" types
template<typename... Ts>
class type {
// apply each template to the corresponding base type
// the resulting list of base types is passed to std::tuple
// there's a "field" for each Cs<Ts> inside the tuple
std::tuple<Cs<Ts>...> parts;
public:
// hopefully self-explanatory
type(Cs<Ts>... parts) : parts(parts...) { }
// return the result of calling foo() on the container identified by C
template<template<typename> typename C>
auto foo() {
// we know the list of all the Cs,
// so just find the index of C in it and pass that to std::get
// then pass in parts to get the desired object, then call foo()
return std::get<index_of_v<suspend<C>, suspend<Cs>...>>(parts).foo();
}
};
// there's no luck for deducing Cs, since in order to *get* to types we need to specify Cs
// (choices::type is not a thing, you always write choices<Cs...>::type)
// But you can deduce `Ts`, by looking at the things contained in each container
// so, deduction guide
template<typename... Ts>
type(Cs<Ts>...) -> type<Ts...>;
};
There's nothing particularly interesting going on here. You have a list of container templates, and a list of contained types, and each object contains a tuple of objects of the desired types. Something has to be passed to foo so it knows which object to retrieve; the sanest option is the type of the container. You could also use the contained type, but apparently those aren't unique, so that wouldn't really work. You could also just pass the index directly. Since we don't know the whole type of the object, there's an auxiliary index_of type function to find the necessary index to pass to std::get. For simplicity, index_of only takes typename arguments. Since we want to search for a template<typename> typename in a list of such, all of them are wrapped in suspend to make it work.
You recover your two A types like this:
template<typename T>
struct B {
T x;
B(T x) : x(x) { }
T foo() { return x; }
};
using A1 = choices<B>;
void demonstration1() {
A1::type a(B(5)); // note the deduction guide at work
std::cout << "B: " << a.foo<B>() << "\n";
}
template<typename T>
struct C {
T x;
C(T x) : x(x) { }
T foo() { return -x; } // keeping it interesting
};
using A2 = choices<B, C>;
void demonstration2() {
A2::type a(B('a'), C(5));
std::cout << "B: " << a.foo<B>() << "; C: " << a.foo<C>() << "\n";
}
Godbolt

Related

Is this allowed syntax for defaulted template argument?

I want to be able to have a template type argument as empty, which case the class just has an empty T, or U, or whatever. I tried to do this defaulting the template argument to a simple lambda:
template <typename U = decltype([]() {})>
On Visual Studio 2019 16.9.3 I don't get the right result when I query the type of U, but by trying to compile on OnlineGDB I found out that this probably shouldn't be compiling anyway because the defaulted argument is defining a new type:
#include <iostream>
template <typename T, typename U = decltype([]() { }) >
struct Planet
{
T T_obj;
[[no_unique_address]] U U_obj;
};
int main()
{
Planet<int, char> planet;
std::cout << "Typename = " << typeid([]() {}).name() << '\n'; // Type here is a lambda, which is correct
std::cout << "Typename of U = " << typeid(planet.U_obj).name() << '\n'; // Type here is int, why?
std::cout << "Typename of U = " << typeid(Planet<int>::U_obj).name() << '\n'; // Type here is int, why?
}
Is this just some sort of bug? If I cannot do 'decltype([] () {})' then I can just define 'struct EmptyType{};' just above the template without a problem.
In C++17, a lambda may not appear in an unevaluated context. This restriction was lifted in C++20, but your compiler might not have implemented this feature yet (even if it already supports [[no_unique_address]], another C++20 feature), so you need to upgrade your compiler. And in any case I would recommend not writing code like this, because it would mean that Planet<int> would not be the same type as Planet<int> (even within the same TU), as a new lambda type will be created every time the default argument is used. void might work better as the default type, and you can give U_obj some private empty class type in the case where U is void.
The correct answer to the question was given by Brian Bi (see above). This post is just a follow-up that started from a comment discussion and was requested by the original poster to be elaborated in more detail and might be helpful to somebody trying achieve the same behaviour. Therefore I decided to post it over here rather than somewhere hidden in the comment. The solutions are not equivalent (e.g. stack vs heap allocation) and partially also have a different interface.
Dummy struct
One possible solution would be to simply pass a dummy struct as a default parameter as follows (Wandbox)
struct DummyStruct {
};
template <typename T, typename U = DummyStruct>
struct Planet {
Planet(T const t, U const u = {})
: t_{t}, u_{u} {
return;
}
T t_;
U u_;
};
Pointer to void
Similarly one could use plain or smart pointers and a default type void. The given constructor will not work with template deduction but you can always write a constructor with smart pointers (Wandbox).
template <typename T, typename U = void>
struct Planet {
// Trick to disable this constructor for type void in order to avoid compilation error
template<typename T1 = T, typename U1 = U,
typename std::enable_if_t<!std::is_same_v<U1,void> && std::is_same_v<U1,U>>* = nullptr>
Planet(T1 const& t, U1 const& u)
: t_{std::make_shared<T>(t)}, u_{std::make_shared<U>(u)} {
return;
}
Planet(T const& t)
: t_{std::make_shared<T>(t)}, u_{nullptr} {
return;
}
std::shared_ptr<T> t_;
std::shared_ptr<U> u_;
};
Variadic templates and tuples
One might as well use variadic templates in combination with tuples and then access them with std::get(...) but the usage might be a bit inconvenient (Wandbox)
template <typename... Ts>
struct Planet {
Planet(Ts const&... t)
: t_{std::make_tuple(t...)} {
return;
}
std::tuple<Ts...> t_;
};
Polymorphic vector
Another possibility in some special cases where the involved containers are derived from a common base class and therefore share a common interface is using a std::vector of smart pointers such as (Wandbox):
struct Parent {
virtual void update() = 0;
};
struct Child: public Parent {
void update() override {
// Do something
return;
}
};
struct Planet {
template <typename... Ts>
Planet(std::shared_ptr<Ts>&... t)
: t_{t...} {
// Static assertion with fold expression
static_assert((std::is_base_of_v<Parent, Ts> && ...), "Template arguments must inherit from 'Parent'.");
return;
}
std::vector<std::shared_ptr<Parent>> t_;
};

C++ nested templates structs

So i have problem with code like this:
I have struct like this
template <int N>
struct Inner
{
enum
{
val = 2*N
};
};
And i want to achive sth like this:
int v = Outer<Inner<4>>::val;
int b = Outer<false>::val;
cout<< v <<endl;
cout<< b <<endl;
My goal is to created "Outer" struct which takes bool or Inner<int N> and set Outer::val to Inner::val or bool
So i have created sth like this (not working):
template <bool B>
struct Outer
{
enum
{
val = B
};
};
template <Inner<int> I>
struct Outer
{
enum
{
val = I::val
};
};
Whats wrong with this and how to fix that?
(I have seen some similar questions, but still can't apply this to my problem)
There are some problems in your code.
First of all: you define two different Outer structs
template <bool B>
struct Outer
{ /* ... */ };
template <Inner<int> I>
struct Outer
{ /* ... */ };
And you can't.
If you want, you can declare an Outer struct and two specializations, but you have to decide what type of template argument Outer has to receive.
Because, looking at your desiderata,
int v = Outer<Inner<4>>::val;
int b = Outer<false>::val;
you want pass to it a type in one case (Inner<4>) and a value in the other case. And you can't.
You have to decide if Outer receive a type or a value. Before C++17, if receive a value, you have to decide the type of the value; starting from C++17, you can declare Outer as receiving a value of a generic type (auto as type of the value).
Problem: a value of Inner<int> can't be a template parameter (but see also the Michael Kenzel's answer, that show a possible C++20 solution based on template values arguments).
So the only solution I see (before C++20) is declare Outer as receiving a type
template <typename>
struct Outer;
Then you can define a Outer specialization for Inner types
template <int N>
struct Outer<Inner<N>>
{ enum { val = Inner<N>::val }; }; // or simply enum { val = N };
For bool values, you have to wrap they in a class; I suggest (starting from C++11) the use of the standard class std::integral_constant and the definition of the following Outer specialization
template <bool B>
struct Outer<std::integral_constant<bool, B>>
{ enum { val = B }; };
The use is as follows
int v = Outer<Inner<4>>::val;
int b = Outer<std::integral_constant<bool, false>>::val;
std::cout << v << std::endl;
std::cout << b << std::endl;
You can also use std::false_type defining b
int b = Outer<std::false_type>::val;
and, starting from C++17, also std::bool_constant (a shorthand for std::integral_constant for bool values)
int b = Outer<std::bool_constant<false>>::val;
A template parameter can be either a type, a value (non-type), or a template [temp.param]. What you're trying to achieve would require your template Outer to have a parameter that can be either a type or a value. Unfortunately, this is not possible.
What you could do is wrap your bool value in a type:
template <bool b>
struct InnerBoolean
{
static constexpr bool val = b;
};
and then have one common definition for Outer
template <typename T>
struct Outer
{
enum
{
value = T::val
};
};
and then use Outer<Inner<4>> and Outer<InnerBoolean<False>>.
Rather than write your own wrapper, if you rename val to value, you can use the wrappers that the standard library provides in std::bool_constant and std::true_type and std::false_type.
While up to C++17, a non-type template parameter cannot be of class type [temp.param]/4, C++20 will lift this restriction and allow template parameters of any literal type. Thus, as long as Inner can be a literal type, you will be able to just pass a value of type Inner directly and use an auto template parameter:
struct Inner
{
int N;
constexpr Inner(int N) : N(N) {}
constexpr operator int() const { return 2*N; }
};
template <auto val>
struct Outer
{
enum
{
value = val
};
};
auto a = Outer<Inner(4)>::value;
auto c = Outer<false>::value;

What else do I need to use variadic template inheritance to create lambda overloads?

I understand the basic concept of using the recursive nature of variadic template parameters and a specific template instantiation to sort of "eat" my way through the parameter list, one by one.
I understand that lambdas can be written to take certain types and then return certain types. Keep in mind that I'm still learning C++14 and C++11, so I haven't mastered one or the other.
Here was my attempt after at looking at other Stack Overflow questions:
// For std::string
#include <string>
// For std::cout
#include <iostream>
//Create a generalized list instantiation
template <typename ... F>
struct overload : public F... {
overload(F... f) : F(f)... {}
};
//Create an specific end-case, where we directly
//inherit the () operator in order to inherit
//multiple () overloads
template <typename F>
struct overload : F {
using F::operator();
};
//template function to create an overload
template <class... F>
auto make_overload(F... f) {
return (f...);
}
int main() {
auto f = [](int x,int y) -> int {
return x+y;
};
auto g = [](double x,double y) -> int {
return std::ftoi(x+y);
};
auto h = [](std::string x,std::string y) -> int {
return std::stoi(x+y);
};
//Ah, but this is a function.
auto fgh = make_overload(f,g,h);
std::cout << (fgh(1,2)) << std::endl;
std::cout << (fgh(1.5,2.5)) << std::endl;
std::cout << (fgh("bob","larry")) << std::endl;
}
Coliru: http://coliru.stacked-crooked.com/a/5df2919ccf9e99a6
What am I conceptually missing here? Other answers might succinctly answer this problem at face value, but I'm looking for an explanation why the answer eludes my thinking. If I understand that I need to do using F::operator() to inherit the operators and I correctly state that the return and parameter types are different, what else do I need to do to make this work?
Here's my train of thought:
Create a general variadic template base class.
Create a specific template case to overload a specific lambda's operator().
Create a helper function to take a variadic template argument list and then use it to construct the "overload" class.
Ensure that the types are unambiguous.
You didn't actually recurse.
// primary template; not defined.
template <class... F> struct overload;
// recursive case; inherit from the first and overload<rest...>
template<class F1, class... F>
struct overload<F1, F...> : F1, overload<F...> {
overload(F1 f1, F... f) : F1(f1), overload<F...>(f...) {}
// bring all operator()s from the bases into the derived class
using F1::operator();
using overload<F...>::operator();
};
// Base case of recursion
template <class F>
struct overload<F> : F {
overload(F f) : F(f) {}
using F::operator();
};

How do I call a constructor with arguments for types instantiated in a boost fusion container?

Here is a simple example which illustrates the issue:
// this class requires a parameter to its constructor
class A
{
A() = delete;
A(int x) {}
};
using B = A;
using C = A;
using Vector = boost::fusion::vector<A, B, C>;
// somewhere else in my code
template <typename FusionVector>
void fun()
{
// I need to instantiate this vector here, and initialise each element with some integer.
// I _know_ what parameters the types take, but I have no idea what types they are
// or how many of them there are. Can I do this?
FusionVector vec; // this won't work...
}
int main()
{
int x = 10; // I want to construct all types in Vector with this value
fun<Vector>(); // how do I do that?
}
Like the commenters said, the question arises where you'll get the values from. Now, I assumed you mean you know what values to pass for "default" construction (even though there is not, technically, a default constructor).
For this purpose, you can use a trait:
namespace mytraits {
template <typename T>
struct elem_construct {
static T call() { return T(); }
};
}
As you can see, the default implementation of elem_construct::call just calls the default constructor [1].
Now, how to invoke this trait for each vector element?
In a perfect world (read: where Boost Fusion was using variadics in true C++11 style), it would be simple
template <typename FusionVector> struct fun_helper;
template <typename... Ts>
struct fun_helper<boost::fusion::vector<Ts...> > {
static typename boost::fusion::vector<Ts...> call() {
return { std::move(mytraits::elem_construct<Ts>::call())... };
}
};
However, this fails on the reason that Boost Fusion supports C++03 and there are no true variadics. Therefore, 10 [2] template arguments are deduced, and we would end up passing 3 A instances as well as 7 instances of type boost::fusion::void_... Yeah. That doesn't really work.
So jumping through some hoops to placate the compiler:
namespace detail {
template <typename... Ts>
struct internal_fun_helper {
static typename boost::fusion::vector<Ts...> call() {
return boost::fusion::vector<Ts...> (std::move(mytraits::elem_construct<Ts>::call())...);
}
};
// helpers that detect the real cardinality of vectors even in the presence of C++03 faux variadics
// {{{ plumbing
template <typename FusionVector> struct fun_helper;
template <typename T1>
struct fun_helper<boost::fusion::vector<T1> > {
static typename boost::fusion::vector<T1> call() { return internal_fun_helper<T1>::call(); }
};
template <typename T1, typename T2>
struct fun_helper<boost::fusion::vector<T1,T2> > {
static typename boost::fusion::vector<T1,T2> call() { return internal_fun_helper<T1,T2>::call(); }
};
// etc. - you get the idea
Now you can just dispatch the specializations using fun:
template <typename FusionVector> FusionVector fun() {
return detail::fun_helper<FusionVector>::call();
}
And it all works, see this demo Live On Coliru
// here's the magic that tells our `fun` factory how to construct A
namespace mytraits {
template <>
struct elem_construct<A> { static A call() { return A(42); } };
}
#include <boost/phoenix.hpp>
using boost::phoenix::arg_names::arg1;
int main()
{
boost::fusion::for_each(fun<boost::fusion::vector<A, B, C>>(), std::cout << arg1 << "\n");
}
Printing out
A[42]
A[42]
A[42]
[1] (or value initializes T for primitive types and aggregates).
[2] depending on preprocessor defines, but 10 is the default limit (see FUSION_MAX_VECTOR_SIZE)

Function template specialization with overloads with different number of parameters

Consider the following (invalid) code sample:
// a: base template for function with only one parameter
template<typename T>
void f(T t) { }
// b: base tempalte for function with two parameters
template<typename T1, typename T2>
void f(T1 t1, T2 t2) { }
// c: specialization of a for T = int
template<>
void f<int>(int i) { }
// d: specialization for b with T1 = int - INVALID
template<typename T2>
void f<int, T2>(int i, T2 t2) { }
int main() {
f(true); // should call a
f(true, false); // should call b
f(1); // should call c
f(1, false); // should call d
}
I've read this walk-through on why, in general, partial function template specializations won't work, and I think I understand the basic reasoning: there are cases where function template specializations and overloading would make certain calls ambiguous (there are good examples in the article).
However, is there a reason why this specific example wouldn't work, other than "the standard says it shouldn't"? Does anything change if I can guarantee (e.g. with a static_assert) that the base template is never instantiated? Is there any other way to achieve the same effect?
What I actually want to achieve is to create an extendable factory method
template<typename T>
T create();
which also has a few overloads taking input parameters, e.g.
template<typename T, typename TIn>
T create(TIn in);
template<typename T, typename TIn1, typename TIn2>
T create(TIn1 in1, TIn2 in2);
In order to ensure that all necessary factory methods are present, I use static_assert in the function base templates, so that a compiler error is generated if the create method is called with template arguments for which no specialization has been provided.
I want these to be function templates rather than class templates because there will be quite a lot of them, and they will all use input from the same struct hierarchy, so instantiating 10 factories instead of one comes with some overhead that I'd like to avoid (not considering the fact that the code gets much easier to understand this way, if I can just get it to work...).
Is there a way to get around the problem outlined in the first half of this post, in order to achieve what I've tried to get at with the second half?
In response to iavr:
I could do this with plain overloading, which would (given the templates above) give something like
template<typename TIn2>
A create(bool, TIn2);
template<typename TIn2>
A create(int, TIn2);
if I need two different partial specializations with T = A, TIn1 specified and TIn2 still unspecified. This is a problem, since I have some cases (which are really text-book cases for meta-programming and templates) where I know that, for example, one of the arguments will be a std::string, and the other will be of some type that has a property fields and a property grids, which are of types std::vector<field> and std::vector<grid> respectively. I don't know all the types that will ever be supplied as the second argument - I know for sure that there will be more of them than the ones I currently have implemented - but the implementation of the method will be exactly the same.
While writing up this update, I think I've figured out a way to redesign the implementations so that there is no need for the partial specialization - basically, I do the following to cover the case outlined above:
template<>
A create<A, std::vector<field>, std::vector<grid>>(std::vector<field> fs, std::vector<grid> gs);
and then I have to change the calling signature slightly, but that's OK.
I share your concerns that maybe in this particular case there would be no problem having function template partial specializations, but then again, that's the way it is, so what would be your problem using plain overloading?
// a: base template for function with only one parameter
template<typename T>
void f(T t) { }
// b: base template for function with two parameters
template<typename T1, typename T2>
void f(T1 t1, T2 t2) { }
// c: specialization of a for T = int
void f(int i) { }
// d: specialization for b with T1 = int
template<typename T2>
void f(int i, T2 t2) { }
This also takes less typing and I get this is why you don't want to use function objects (which would have partial specialization).
Here is a simple workaround using a class template specialization:
template <typename, typename...>
struct Creator;
template <typename T, typename TIn>
struct Creator<T, TIn>
{
T call(TIn in)
{
// ...
}
};
template<typename T, typename TIn1, typename TIn2>
struct Creator<T, TIn1, TIn2>
{
T call(TIn1 in1, TIn2 in2)
{
// ...
}
};
template <typename R, typename... Arguments>
R Create(Arguments&&... arguments)
{
return Creator<R, Arguments...>::call(std::forward<Arguments>(arguments)...);
}
If you don't want overloading, and want to be able to specialize from a separate file, then I think you should base it on the solution on the link from your question. It involves making a static method on a class that you specialize. From my reading of the question, you're only interested in specializing on the T, not on the number of arguments, which you intend to forward. In C++11, you can do the following:
#include <iostream>
#include <utility>
using namespace std;
template<typename T>
struct factory_impl;
// Left unspecified for now (which causes compliation failure if
// not later specialized
template<typename T, typename... Args>
T create(Args&&... args)
{
return factory_impl<T>::create(std::forward<Args>(args)...);
}
// Note, this can be specified in a header in another translation
// unit. The only requirement is that the specialization
// be defined prior to calling create with the correct value
// of T
template<>
struct factory_impl<int>
{
// int can be constructed with 0 arguments or 1 argument
static int create(int src = 0)
{
return src;
}
};
int main(int argc, char** argv)
{
int i = create<int>();
int j = create<int>(5);
// double d = create<double>(); // Fails to compile
std::cout << i << " " << j << std::endl;
return 0;
}
Live example http://ideone.com/7a3uRZ
Edit: In response to your question, you could also make create a member function of a class, and pass along some of that data with the call or take action before or after
struct MyFactory
{
template<typename T, typename... Args>
T create(Args&&... args)
{
T ret = factory_impl<T>::create(data, std::forward<Args>(args)...);
// do something with ret
return ret;
}
Foo data; // Example
};