Here's what I'm trying to do:
template <typename T> struct Model
{
vector<T> vertices ;
#if T has a .normal member
void transform( Matrix m )
{
each vertex in vertices
{
vertex.pos = m * vertex.pos ;
vertex.normal = m * vertex.normal ;
}
}
#endif
#if T has NO .normal member
void transform( Matrix m )
{
each vertex in vertices
{
vertex.pos = m * vertex.pos ;
}
}
#endif
} ;
I've seen examples of using enable_if, but I cannot understand how to apply enable_if to this problem, or if it even can be applied.
This has become way easier with C++11.
template <typename T> struct Model
{
vector<T> vertices;
void transform( Matrix m )
{
for(auto &&vertex : vertices)
{
vertex.pos = m * vertex.pos;
modifyNormal(vertex, m, special_());
}
}
private:
struct general_ {};
struct special_ : general_ {};
template<typename> struct int_ { typedef int type; };
template<typename Lhs, typename Rhs,
typename int_<decltype(Lhs::normal)>::type = 0>
void modifyNormal(Lhs &&lhs, Rhs &&rhs, special_) {
lhs.normal = rhs * lhs.normal;
}
template<typename Lhs, typename Rhs>
void modifyNormal(Lhs &&lhs, Rhs &&rhs, general_) {
// do nothing
}
};
Things to note:
You can name non-static data members in decltype and sizeof without needing an object.
You can apply extended SFINAE. Basically any expression can be checked and if it is not valid when the arguments are substituted, the template is ignored.
I know this question already has some answers but I think my solution to this problem is a bit different and could help someone.
The following example checks whether passed type contains c_str() function member:
template <typename, typename = void>
struct has_c_str : false_type {};
template <typename T>
struct has_c_str<T, void_t<decltype(&T::c_str)>> : std::is_same<char const*, decltype(declval<T>().c_str())>
{};
template <typename StringType,
typename std::enable_if<has_c_str<StringType>::value, StringType>::type* = nullptr>
bool setByString(StringType const& value) {
// use value.c_str()
}
In case there is a need to perform checks whether passed type contains specific data member, following can be used:
template <typename, typename = void>
struct has_field : std::false_type {};
template <typename T>
struct has_field<T, std::void_t<decltype(T::field)>> : std::is_convertible<decltype(T::field), long>
{};
template <typename T,
typename std::enable_if<has_field<T>::value, T>::type* = nullptr>
void fun(T const& value) {
// use value.field ...
}
UPDATE C++20
C++20 introduced constraints and concepts, core language features in this C++ version.
If we want to check whether template parameter contains c_str member function, then, the following will do the work:
template<typename T>
concept HasCStr = requires(T t) { t.c_str(); };
template <HasCStr StringType>
void setByString(StringType const& value) {
// use value.c_str()
}
Furthermore, if we want to check if the data member, which is convertible to long, exists, following can be used:
template<typename T>
concept HasField = requires(T t) {
{ t.field } -> std::convertible_to<long>;
};
template <HasField T>
void fun(T const& value) {
// use value.field
}
By using C++20, we get much shorter and much more readable code that clearly expresses it's functionality.
You need a meta function to detect your member so that you can use enable_if. The idiom to do this is called Member Detector. It's a bit tricky, but it can be done!
This isn't an answer to your exact case, but it is an alternative answer to the question title and problem in general.
#include <iostream>
#include <vector>
struct Foo {
size_t length() { return 5; }
};
struct Bar {
void length();
};
template <typename R, bool result = std::is_same<decltype(((R*)nullptr)->length()), size_t>::value>
constexpr bool hasLengthHelper(int) {
return result;
}
template <typename R>
constexpr bool hasLengthHelper(...) { return false; }
template <typename R>
constexpr bool hasLength() {
return hasLengthHelper<R>(0);
}
// function is only valid if `.length()` is present, with return type `size_t`
template <typename R>
typename std::enable_if<hasLength<R>(), size_t>::type lengthOf (R r) {
return r.length();
}
int main() {
std::cout <<
hasLength<Foo>() << "; " <<
hasLength<std::vector<int>>() << "; " <<
hasLength<Bar>() << ";" <<
lengthOf(Foo()) <<
std::endl;
// 1; 0; 0; 5
return 0;
}
Relevant https://ideone.com/utZqjk.
Credits to dyreshark on the freenode IRC #c++.
template<
typename HTYPE,
typename = std::enable_if_t<std::is_same<decltype(HTYPE::var1), decltype(HTYPE::var1)>::value>
>
static void close_release
(HTYPE* ptr) {
ptr->var1;
}
Using enable_if and decltype to let compiler to check variable, hope to help.
While C++20's requires keyword has been mentioned, the code that's provided is still too complex for your needs, requiring the creation of a separate function for each case. Here's much simpler code for your use case, where a single function implementation suffices:
template <typename T> struct Model
{
vector<T> vertices ;
void transform( Matrix m )
{
each vertex in vertices
{
vertex.pos = m * vertex.pos ;
if constexpr (requires { &vertex.normal; })
vertex.normal = m * vertex.normal ;
}
}
} ;
Notes:
All the trick is on the if constexpr line. I've left your pseudo code as is, but removed the redundancy and added the if constexpr line.
The requires expression I've added simply attempts to access the address of the normal member, and evaluates to false if the expression is invalid. You could really use any expression that will succeed if normal is defined and fail if it's not.
For classes that do have normal, make sure the member is accessible from this code (e.g. it's either public or an appropriate friendship is specified). Otherwise the code would ignore the normal member as if it didn't exist at all.
See the "Simple requirements" section at https://en.cppreference.com/w/cpp/language/constraints for more information.
I know that it's little late, however...
typedef int Matrix;
struct NormalVertex {
int pos;
int normal;
};
struct Vertex {
int pos;
};
template <typename T> struct Model
{
typedef int No;
typedef char Yes;
template<typename U> static decltype (declval<U>().normal, Yes()) has_normal(U a);
static No has_normal(...);
vector<T> vertices ;
template <typename U = T>
typename enable_if<sizeof(has_normal(declval<U>())) == sizeof(Yes), void>::type
transform( Matrix m )
{
std::cout << "has .normal" << std::endl;
for (auto vertex : vertices)
{
vertex.pos = m * vertex.pos ;
vertex.normal = m * vertex.normal ;
}
}
template <typename U = T>
typename enable_if<sizeof(has_normal(declval<U>())) == sizeof(No), void>::type
transform( Matrix m )
{
std::cout << "has no .normal" << std::endl;
for (auto vertex : vertices)
{
vertex.pos = m * vertex.pos ;
}
}
} ;
int main()
{
Matrix matrix;
Model <NormalVertex> normal_model;
Vertex simple_vertex;
Model <Vertex> simple_model;
simple_model.transform(matrix);
normal_model.transform(matrix);
return 0;
}
I had a similar issue and my solution was to use boost's BOOST_TTI_HAS_MEMBER_DATA macro.
#include <boost/tti/has_member_data.hpp>
BOOST_TTI_HAS_MEMBER_DATA(normal)
template <typename T> struct Model
{
vector<T> vertices;
static constexpr bool hasNormal = has_member_data_normal<T, double>::value;
template<bool B = hasNormal, std::enable_if_t<B, int> = 0>
void transform( Matrix m )
{
for(auto&& vertex : vertices)
{
vertex.pos = m * vertex.pos ;
vertex.normal = m * vertex.normal ;
}
}
template<bool B = hasNormal, std::enable_if_t<!B, int> = 0>
void transform( Matrix m )
{
for(auto&& vertex : vertices)
{
vertex.pos = m * vertex.pos ;
}
}
};
If you don't want to be dependent on boost, then you can use #ltjax's answer to create your own has_member_data_normal struct.
Related
I have a template matrix class, e.g. (simplified form):
template<typename Scalar, typename Accessor = GenericAccessor>
class Matrix {
public:
Matrix(size_t num_rows, size_t num_cols, const std::vector<Scalar>& elems)
: num_rows_(num_rows), num_cols_(num_cols),
accessor_(num_rows, num_cols),
storage_(elems) {}
private:
size_t num_rows_;
size_t num_cols_;
Accessor accessor_;
std::vector<Scalar> storage_;
}
This class have Transpose method, which will return a new Matrix with rows and columns swapped and different accessor type.
My current implementation seems not to work, because i try to return a matrix of different specialization:
Matrix Transpose() {
if (dynamic_cast<TransposeAccessor*>(accessor_)) {
return Matrix<Scalar, GenericAccessor>(num_cols_, num_rows_, storage_);
}
return Matrix<Scalar, TransposeAccessor>(num_cols_, num_rows_, storage_);
}
I know that return type should match class specialization, but how can i implement Transpose method properly then?
If I understand your question correctly you want to return Matrix<Scalar, TransposeAccessor > if Accessor = GenericAcessor and the other way round.
One way to do that is to create a helper class template, that allows you to get the opposite type (you might need to choose a better name) for your accessor by specializing the template:
template<typename T>
struct accessor_trait;
template<>
struct accessor_trait<GenericAcessor> {
using oppsite_type = TransposeAccessor;
};
template<>
struct accessor_trait<TransposeAccessor> {
using oppsite_type = GenericAcessor;
};
And then your Transpose function could look like that:
auto Transpose() {
return Matrix<Scalar, typename accessor_trait<Accessor>::oppsite_type>(num_cols_, num_rows_, storage_);
}
If you need to use that oppsite accessor at various places you might want to write it that way:
template<typename Scalar, typename Accessor = GenericAcessor>
class Matrix {
public:
// create a name alias here to be used at other places
using OppsiteAccessor = Matrix<Scalar, typename acessor_trait<Accessor>::oppsite_type>;
Matrix(std::size_t num_rows, std::size_t num_cols, const std::vector<Scalar>& elems)
: num_rows_(num_rows), num_cols_(num_cols),
accessor_(num_rows, num_cols),
storage_(elems) {}
Matrix<Scalar, OppsiteAccessor> Transpose() {
return Matrix<Scalar, OppsiteAccessor>(num_cols_, num_rows_, storage_);
}
private:
std::size_t num_rows_;
std::size_t num_cols_;
Accessor accessor_;
std::vector<Scalar> storage_;
};
'Matrix' refers to current context here, i.e. you declared function to return one type and you're trying to return two different types. I assume they aren't related. You have to perform choice statically, i.e. to have two separate specializations of 'Transpose'. If accessor can be changed dynamically, then you gotta do some type erasure.
This is out of "crazy ideas" which probably should not be followed.
#include <iostream>
#include <concepts>
struct GenericAccesstor {};
struct TransposeAccesstor {};
template<typename T>
concept GenericAccess = requires(T a) {
{a} -> std::convertible_to<GenericAccesstor>;
};
template<typename T>
concept TransposedAcccess = requires(T a) {
{a} -> std::convertible_to<TransposeAccesstor>;
};
template < typename Scalar, typename Accessor = GenericAccesstor >
struct Matrix {
using access_type = Accessor;
template < GenericAccess A = Accessor >
auto Transpose() -> Matrix<Scalar, TransposeAccesstor >
{ return Matrix<Scalar, TransposeAccesstor>(); }
template < TransposedAcccess A = Accessor >
auto Transpose() -> Matrix<Scalar, GenericAccesstor >
{ return Matrix<Scalar, GenericAccesstor>(); }
};
We can check how it works:
int main()
{
Matrix<int, GenericAccesstor> m;
auto m2 = m.Transpose();
auto m3 = m2.Transpose();
std::cout << typeid(decltype(m)::access_type).name() << std::endl;
std::cout << typeid(decltype(m2)::access_type).name() << std::endl;
std::cout << typeid(decltype(m3)::access_type).name() << std::endl;
}
/** Output:
16GenericAccesstor
18TransposeAccesstor
16GenericAccesstor */
Tbh, I wouldn't have built Matrix that way at all, because it sound like transposed matrix is a separate type from original one, so I am not sure how good or bad that would be in full implementation.
auto Transpose() {
if constexpr (std::is_same_v<TransposeAccessor,decltype(accessor_)>) {
return Matrix<Scalar, GenericAccessor>(num_cols_, num_rows_, storage_);
} else {
return Matrix<Scalar, TransposeAccessor>(num_cols_, num_rows_, storage_);
}
}
At work, I ran into a situation where the best type to describe the result returned from a function would be std::variant<uint64_t, uint64_t> - of course, this isn't valid C++, because you can't have two variants of the same type. I could represent this as a std::pair<bool, uint64_t>, or where the first element of the pair is an enum, but this is a special case; a std::variant<uint64_t, uint64_t, bool> isn't so neatly representable, and my functional programming background really made me want Either - so I went to try to implement it, using the Visitor pattern as I have been able to do in other languages without native support for sum types:
template <typename A, typename B, typename C>
class EitherVisitor {
virtual C onLeft(const A& left) = 0;
virtual C onRight(const B& right) = 0;
};
template <typename A, typename B>
class Either {
template <typename C>
virtual C Accept(EitherVisitor<A, B, C> visitor) = 0;
};
template <typename A, typename B>
class Left: Either<A, B> {
private:
A value;
public:
Left(const A& valueIn): value(valueIn) {}
template <typename C>
virtual C Accept(EitherVisitor<A, B, C> visitor) {
return visitor.onLeft(value);
}
};
template <typename A, typename B>
class Right: Either<A, B> {
private:
B value;
public:
Right(const B& valueIn): value(valueIn) {}
template <typename C>
virtual C Accept(EitherVisitor<A, B, C> visitor) {
return visitor.onRight(value);
}
};
C++ rejects this, because the template method Accept cannot be virtual. Is there a workaround to this limitation, that would allow me to correctly represent the fundamental sum type in terms of its f-algebra and catamorphism?
Perhaps the simplest solution is a lightweight wrapper around T for Right and Left?
Basically a strong type alias (could also use Boost's strong typedef)
template<class T>
struct Left
{
T val;
};
template<class T>
struct Right
{
T val;
};
And then we can distinguish between them for visitation:
template<class T, class U>
using Either = std::variant<Left<T>, Right<U>>;
Either<int, int> TrySomething()
{
if (rand() % 2 == 0) // get off my case about rand(), I know it's bad
return Left<int>{0};
else
return Right<int>{0};
}
struct visitor
{
template<class T>
void operator()(const Left<T>& val_wrapper)
{
std::cout << "Success! Value is: " << val_wrapper.val << std::endl;
}
template<class T>
void operator()(const Right<T>& val_wrapper)
{
std::cout << "Failure! Value is: " << val_wrapper.val << std::endl;
}
};
int main()
{
visitor v;
for (size_t i = 0; i < 10; ++i)
{
auto res = TrySomething();
std::visit(v, res);
}
}
Demo
std::variant<X,X> is valid C++.
It is a bit awkward to use, because std::visit doesn't give you the index, and std::get<X> won't work either.
The way you can work around this is to create a variant-of-indexes, which is like a strong enum.
template<std::size_t i>
using index_t = std::integral_constant<std::size_t, i>;
template<std::size_t i>
constexpr index_t<i> index = {};
template<std::size_t...Is>
using number = std::variant< index_t<Is>... >;
namespace helpers {
template<class X>
struct number_helper;
template<std::size_t...Is>
struct number_helper<std::index_sequence<Is...>> {
using type=number<Is...>;
};
}
template<std::size_t N>
using alternative = typename helpers::number_helper<std::make_index_sequence<N>>::type;
we can then extract the alternative from a variant:
namespace helpers {
template<class...Ts, std::size_t...Is, class R=alternative<sizeof...(Ts)>>
constexpr R get_alternative( std::variant<Ts...> const& v, std::index_sequence<Is...> ) {
constexpr R retvals[] = {
R(index<Is>)...
};
return retvals[v.index()];
}
}
template<class...Ts>
constexpr alternative<sizeof...(Ts)> get_alternative( std::variant<Ts...> const& v )
{
return helpers::get_alternative(v, std::make_index_sequence<sizeof...(Ts)>{});
}
so now you have a std::variant<int, int>, you can
auto which = get_alternative( var );
and which is a variant, represented at runtime by an integer which is the index of the active type in var. You can:
std::variant<int, int> var( std::in_place_index_t<1>{}, 7 );
auto which = get_alternative( var );
std::visit( [&var](auto I) {
std::cout << std::get<I>(var) << "\n";
}, get_alternative(var) );
and get access to which of the alternative possibilities in var is active with a compile time constant.
The get_alternative(variant), I find, makes variant<X,X,X> much more usable, and fills in the hole I think you might be running into.
Live example.
Now if you don't need a compile-time index of which one is active, you can just call var.index(), and visit via visit( lambda, var ).
When you construct the variant, you do need the compile time index to do a variant<int, int> var( std::in_place_index_t<0>{}, 7 ). The wording is a bit awkward, because while C++ supports variants of multiples of the same type, it considers them a bit less likely than a "standard" disjoint variant outside of generic code.
But I've used this alternative and get_alternative like code to support functional programming like data glue code before.
I am trying to obtain a subset of the variadic arguments of current class wrapper to instantiate a new one
Currently I have this:
// Reference: https://stackoverflow.com/questions/27941661/generating-one-class-member-per-variadic-template-argument
// Template specialization
template<typename T, typename... Next> class VariadicClass;
// Base case extension
template <typename T>
class VariadicClass<T> {
private:
T value_;
protected:
void SetField(T & value) {
value_ = value;
}
T & GetField() {
return value_;
}
};
// Inductive case
template <typename T, typename ... Next>
class VariadicClass : public VariadicClass<T>, public VariadicClass<Next...> {
public:
// Copy the values into the variadic class
template <typename F>
void Set(F f) {
this->VariadicClass<F>::SetField(f);
}
// Retrieve by reference
template <typename F>
F & Get() {
return this->VariadicClass<F>::GetField();
}
};
And what I want to achieve is something along the following:
[C]: A subset of Args...
VariadicClass<[C]> * Filter(VariadicClass<Args...> input) {
return new VariadicClass<[C]>(GetSubsetFrom(input, [C]));
}
VariadicClass<int, bool, char> class1;
VariadicClass<int, bool> * variadic = Filter(class1);
You can assume that each type is only once in the variadic class and that I will always ask for a subset of the current variadic types. I don't know if this is currently possible in C++ 11?
Thank you for your help.
It seems to me that you're trying to reinvent the wheel (where "wheel", in this case, is std::tuple).
Anyway, what you ask seems simple to me
template <typename ... As1, typename ... As2>
VariadicClass<As1...> * Filter(VariadicClass<As2...> in)
{
using unused = int[];
auto ret = new VariadicClass<As1...>();
(void)unused { 0, (ret->template Set<As1>(in.template Get<As1>()), 0)... };
return ret;
}
The problem I see is that the As1... types (the types of the returned VariadicClass) aren't deducible by the returned value, so you can't write
VariadicClass<int, bool> * variadic = Filter(class1);
You have to explicit the As1... types calling Filter(), so
VariadicClass<int, bool> * variadic = Filter<int, bool>(class1);
or, maybe better,
auto variadic = Filter<int, bool>(class1);
The following is a full compiling example
#include <iostream>
template <typename, typename...>
class VariadicClass;
template <typename T>
class VariadicClass<T>
{
private:
T value_;
protected:
void SetField (T & value)
{ value_ = value; }
T & GetField ()
{ return value_; }
};
template <typename T, typename ... Next>
class VariadicClass : public VariadicClass<T>, public VariadicClass<Next...>
{
public:
template <typename F>
void Set (F f)
{ this->VariadicClass<F>::SetField(f); }
template <typename F>
F & Get()
{ return this->VariadicClass<F>::GetField(); }
};
template <typename ... As1, typename ... As2>
VariadicClass<As1...> * Filter(VariadicClass<As2...> in)
{
using unused = int[];
auto ret = new VariadicClass<As1...>();
(void)unused { 0, (ret->template Set<As1>(in.template Get<As1>()), 0)... };
return ret;
}
int main()
{
VariadicClass<int, bool, char> c1;
c1.Set<int>(42);
c1.Set<bool>(true);
c1.Set<char>('Z');
auto pC2 = Filter<int, bool>(c1);
std::cout << pC2->Get<int>() << std::endl;
std::cout << pC2->Get<bool>() << std::endl;
delete pC2;
}
Off Topic Unrequested Suggestion: you're using C++11 so... try to avoid the direct use of pointer and try to use smart pointers (std::unique_ptr, std::shared_ptr, etc.) instead.
First of all I think you shouldn't write your own variadic class as we already have std::tuplein place.
I wonder that you sit on c++11because it is quite old. Even c++14is outdated but if you can switch, the solution is very simple:
template < typename DATA, typename FILTER, std::size_t... Is>
auto Subset_Impl( const DATA& data, FILTER& filter, std::index_sequence<Is...> )
{
filter = { std::get< typename std::remove_reference<decltype( std::get< Is >( filter ))>::type>( data )... };
}
template < typename DATA, typename FILTER, typename IDC = std::make_index_sequence<std::tuple_size<FILTER>::value >>
auto Subset( const DATA& data, FILTER& filter )
{
return Subset_Impl( data, filter, IDC{} );
}
int main()
{
std::tuple< int, float, std::string, char > data { 1, 2.2, "Hallo", 'c' };
std::tuple< float, char > filter;
Subset( data, filter );
std::cout << std::get<0>( filter ) << " " << std::get<1>( filter ) << std::endl;
}
If you really want sit on outdated standards, you can easily implement the missing parts from the standard library your self. One related question is answered here: get part of std::tuple
How the helper templates are defined can also be seen on: https://en.cppreference.com/w/cpp/utility/integer_sequence
I have this code:
template<class T1, class T2>
class Pair
{
private:
T1 first;
T2 second;
public:
void SetFirst(T1 first)
{
this.first = first;
}
void SetSecond(T2 second)
{
this.second = second;
}
T1 GetFirst()
{
return first;
}
T2 GetSecond()
{
return second;
}
};
How could I implement two single methods SetValue() and GetValue(), instead of the four I have, that decides depending on parameters which generic type that should be used? For instance I'm thinking the GetValue() method could take an int parameter of either 1 or 2 and depending on the number, return either a variable of type T1 or T2. But I don't know the return type beforehand so is there anyway to solve this?
Not sure to understand what do you want and not exactly what you asked but...
I propose the use of a wrapper base class defined as follows
template <typename T>
class wrap
{
private:
T elem;
public:
void set (T const & t)
{ elem = t; }
T get () const
{ return elem; }
};
Now your class can be defined as
template <typename T1, typename T2>
struct Pair : wrap<T1>, wrap<T2>
{
template <typename T>
void set (T const & t)
{ wrap<T>::set(t); }
template <typename T>
T get () const
{ return wrap<T>::get(); }
};
or, if you can use C++11 and variadic templates and if you define a type traits getType to get the Nth type of a list,
template <std::size_t I, typename, typename ... Ts>
struct getType
{ using type = typename getType<I-1U, Ts...>::type; };
template <typename T, typename ... Ts>
struct getType<0U, T, Ts...>
{ using type = T; };
you can define Pair in a more flexible way as follows
template <typename ... Ts>
struct Pair : wrap<Ts>...
{
template <typename T>
void set (T const & t)
{ wrap<T>::set(t); }
template <std::size_t N, typename T>
void set (T const & t)
{ wrap<typename getType<N, Ts...>::type>::set(t); }
template <typename T>
T get () const
{ return wrap<T>::get(); }
template <std::size_t N>
typename getType<N, Ts...>::type get ()
{ return wrap<typename getType<N, Ts...>::type>::get(); }
};
Now the argument of set() can select the correct base class and the correct base element
Pair<int, long> p;
p.set(0); // set the int elem
p.set(1L); // set the long elem
otherwise, via index, you can write
p.set<0U>(3); // set the 1st (int) elem
p.set<1U>(4); // set the 2nd (long) elem
Unfortunately, the get() doesn't receive an argument, so the type have to be explicited (via type or via index)
p.get<int>(); // get the int elem value
p.get<long>(); // get the long elem value
p.get<0U>(); // get the 1st (int) elem value
p.get<1U>(); // get the 2nd (long) elem value
Obviously, this didn't work when T1 is equal to T2
The following is a (C++11) full working example
#include <iostream>
template <std::size_t I, typename, typename ... Ts>
struct getType
{ using type = typename getType<I-1U, Ts...>::type; };
template <typename T, typename ... Ts>
struct getType<0U, T, Ts...>
{ using type = T; };
template <typename T>
class wrap
{
private:
T elem;
public:
void set (T const & t)
{ elem = t; }
T get () const
{ return elem; }
};
template <typename ... Ts>
struct Pair : wrap<Ts>...
{
template <typename T>
void set (T const & t)
{ wrap<T>::set(t); }
template <std::size_t N, typename T>
void set (T const & t)
{ wrap<typename getType<N, Ts...>::type>::set(t); }
template <typename T>
T get () const
{ return wrap<T>::get(); }
template <std::size_t N>
typename getType<N, Ts...>::type get ()
{ return wrap<typename getType<N, Ts...>::type>::get(); }
};
int main()
{
//Pair<int, int> p; compilation error
Pair<int, long, long long> p;
p.set(0);
p.set(1L);
p.set(2LL);
std::cout << p.get<int>() << std::endl; // print 0
std::cout << p.get<long>() << std::endl; // print 1
std::cout << p.get<long long>() << std::endl; // print 2
p.set<0U>(3);
p.set<1U>(4);
p.set<2U>(5);
std::cout << p.get<0U>() << std::endl; // print 3
std::cout << p.get<1U>() << std::endl; // print 4
std::cout << p.get<2U>() << std::endl; // print 5
}
C++ is statically typed, so the argument given must be a template-argument instead a function-argument.
And while it will look like just one function each to the user, it's really two.
template <int i = 1> auto GetValue() -> std::enable_if_t<i == 1, T1> { return first; }
template <int i = 2> auto GetValue() -> std::enable_if_t<i == 2, T2> { return second; }
template <int i = 1> auto SetValue(T1 x) -> std::enable_if_t<i == 1> { first = x; }
template <int i = 2> auto SetValue(T2 x) -> std::enable_if_t<i == 2> { second = x; }
I use SFINAE on the return-type to remove the function from consideration unless the template-argument is right.
For this particular situation, you should definitely prefer std::pair or std::tuple.
You can simply overload SetValue() (provided T1 and T2 can be distinguished, if not you have a compile error):
void SetValue(T1 x)
{ first=x; }
void SetValue(T2 x)
{ second=x; }
Then, the compiler with find the best match for any call, i.e.
Pair<int,double> p;
p.SetValue(0); // sets p.first
p.SetValue(0.0); // sets p.second
With GetValue(), the information of which element you want to retrieve cannot be inferred from something like p.GetValue(), so you must provide it somehow. There are several options, such as
template<typename T>
std::enable_if_t<std::is_same<T,T1>,T>
GetValue() const
{ return first; }
template<typename T>
std::enable_if_t<std::is_same<T,T2>,T>
GetValue() const
{ return second; }
to be used like
auto a = p.GetValue<int>();
auto b = p.GetValue<double>();
but your initial version is good enough.
Suppose that we have the vector class below which has been shortened to minimum to showcase the question.
template <typename T>
class VectorT : private std::vector<T>
{
using vec = std::vector<T>;
public:
using vec::operator[];
using vec::push_back;
using vec::at;
using vec::emplace_back;
// not sure if this is the beast way to check if my T is really a unique_ptr
template<typename Q = T>
typename Q::element_type* operator[](const size_t _Pos) const { return at(_Pos).get(); }
};
Is there any way to check if T is a unique_ptr and if yes to add an operator[] to return the unique_ptr::element_type*. At the same time though the normal operator[] should also work.
VectorT<std::unique_ptr<int>> uptr_v;
uptr_v.emplace_back(make_unique<int>(1));
//int* p1 = uptr_v[0]; // works fine if using vec::operator[]; is commented out
// then of course it wont work for the normal case
//std::cout << *p1;
VectorT<int*> v;
v.emplace_back(uptr_v[0].get());
int *p2 = v[0];
std::cout << *p2;
Any way to achieve something like that ?
Edited:
The reason I am asking for this is cause I can have say my container
class MyVec: public VectorT<std::unique_ptr<SomeClass>>
but I can also have a
class MyVecView: public VectorT<SomeClass*>
Both classes will pretty much have identical functions. So I am trying to avoid duplication by doing something like
template<typename T>
void doSomething(VectorT<T>& vec)
{
SomeClass* tmp = nullptr;
for (size_t i = 0; i < vec.size(); ++i)
{
tmp = vec[i]; // this has to work though
....
}
}
Then of course I can
MyVec::doSomething(){doSomething(*this);}
MyVecView::doSomething(){doSomething(*this);}
which of course means that the operator[] has to work for both cases
The goal here is to have only one operator[]. Techniques with more than one operator[] violate DRY (don't repeat yourself), and it is hard to avoid having a template method whose body would not compile if instantiated (which, under a strict reading of the standard, could result in your code being ill-formed).
So what I'd do is model the "turn something into a pointer" like this:
namespace details {
template<class T>
struct plain_ptr_t;
//specialzation for T*
template<class T>
struct plain_ptr_t<T*> {
T* operator()(T* t)const{return t;}
};
//specialzation for std::unique_ptr
template<class T, class D>
struct plain_ptr_t<std::unique_ptr<T,D>> {
T* operator()(std::unique_ptr<T>const& t)const{return t.get();}
};
//specialzation for std::shared_ptr
template<class T>
struct plain_ptr_t<std::shared_ptr<T>> {
T* operator()(std::shared_ptr<T>const& t)const{return t.get();}
};
}
struct plain_ptr {
template<class T>
typename std::result_of< details::plain_ptr_t<T>( T const& ) >::type
operator()( T const& t ) const {
return details::plain_ptr_t<T>{}( t );
}
};
now plain_ptr is a functor that maps smart pointers to plain pointers, and pointers to pointers.
It rejects things that aren't pointers. You could change it to just pass them through if you like, but it takes a bit of care.
We then use them to improve your operator[]:
typename std::result_of< plain_ptr(typename vec::value_type const&)>::type
operator[](size_t pos) const {
return plain_ptr{}(at(pos));
}
notice that it is no longer a template.
live example.
template<typename T> struct unique_ptr_type { };
template<typename T> struct unique_ptr_type<std::unique_ptr<T>> { using type = T; };
namespace detail {
template<typename T> std::false_type is_unique_ptr(T const&);
template<typename T> std::true_type is_unique_ptr(std::unique_ptr<T> const&);
}
template<typename T>
using is_unique_ptr = decltype(detail::is_unique_ptr(std::declval<T>()));
template<typename T>
class VectorT : std::vector<T> {
using vec = std::vector<T>;
public:
using vec::at;
using vec::emplace_back;
using vec::push_back;
template<typename Q = T,
typename std::enable_if<!is_unique_ptr<Q>::value>::type* = nullptr>
Q& operator [](std::size_t pos) { return vec::operator[](pos); }
template<typename Q = T,
typename std::enable_if<!is_unique_ptr<Q>::value>::type* = nullptr>
Q const& operator [](std::size_t pos) const { return vec::operator[](pos); }
template<typename Q = T,
typename U = typename unique_ptr_type<Q>::type>
U* operator [](std::size_t pos) { return vec::operator[](pos).get(); }
template<typename Q = T,
typename U = typename unique_ptr_type<Q>::type>
U const* operator [](std::size_t pos) const { return vec::operator[](pos).get(); }
};
Online Demo
SFINAE is used to only enable the custom operator[] if T is std::unique_ptr<T>, and to only enable std::vector<T>::operator[] otherwise.
If you insist on plain pointers you could write something like
template<class T> auto plain_ptr(T* p) { return p; }
template<class T> auto plain_ptr(std::unique_ptr<T>& p) { return p.get(); }
and then do
tmp = plain_ptr(vec[i]); // tmp will be SomeClass*
or you could have tmp local:
template<typename T>
void doSomething(VectorT<T>& vec)
{
static_assert(std::is_same<T, SomeClass*>::value ||
std::is_same<T, std::unique_ptr<SomeClass>>::value,
"doSomething expects vectors containing SomeClass!");
for (std::size_t i = 0; i < vec.size(); ++i)
{
auto tmp = vec[i];
// ... (*tmp).foo or tmp->foo ...
}
// or perhaps even
for(auto&& tmp : vec)
{
// ... again (*tmp).foo or tmp->foo ...
}
}