Store or reflect on the "reference level" of a variable - c++

Is there a way in C++ to reflect on the "number of levels of pointer" of a variable, (for example, int* a is 1 level, int** b is 2 levels, and int c is 0 levels)
..Other than using typeid and parsing the string that comes out of that?
The reason I'm asking is I am using pointers to member functions, and I need to know whether to invoke the member function as obj->func() or obj.func(), at compile time really.

If obj is a T**, doing obj.*foo is ill-formed. So you only need to figure out whether it is a pointer or a non-pointer. You can use overloading for this.
template<typename T, typename M> void f(T *obj, M m) { // pointer
(obj->*m)();
}
template<typename T, typename M> void f(T &obj, M m) { // not a pointer
(obj.*m)();
}
This has the drawback that it only works with zero-parameter member function pointers, and it won't return the value (if any) that those functions return. You cannot do the following (which can easily be implemented), because both branches will be type-checked
if(is_pointer(obj)) v = (obj->*m)(arg...); else v = (obj.*m)(args...);
What you can do is to just call a function to dereference your object, if it is a pointer
template<typename T> T &deref(T *t) { return *t; }
template<typename T> T &deref(T &t) { return t; }
Then you can say
v = (deref(obj).*m)(args...);

You could use std::is_pointer from the type_traits header in TR1 (which uses partial specialization to produce an answer), but it might be simpler to just use overload resolution.
Here's an example, assuming void return, no arguments, and no need to handle more than one level of indirection:
template <typename T, typename F>
inline void invoke(T& obj, F func)
{
(obj.*func)();
}
template <typename T, typename F>
inline void invoke(T* obj, F func)
{
(obj->*func)();
}
If you need to handle more than one level of indirection, you can replace the second function with this:
template <typename T, typename F>
inline void invoke(T* obj, F func)
{
invoke(*obj, func);
}
This will recursively strip off levels of indirection until you end up with something that you can invoke your member function on.

Partial template specialization will tell you quite easily:
template<typename T>
struct PtrLevel
{
enum { value = 0 };
};
template<typename TTarget>
struct PtrLevel<TTarget*>
{
enum { value = PtrLevel<TTarget>::value + 1 };
};
Demonstration: http://ideone.com/ZPH8X
In C++0x, you could probably use decltype and SFINAE to handle smart pointers in such a way that they are reported as having a non-zero pointer level.

A bit of template magic to the rescue:
template<typename T>
struct Depth { enum DepthEnum { value = 0 };};
template<typename T>
struct Depth<T*> { enum DepthEnum{ value = Depth<T>::value +1 };};
template<typename T>
size_t getDepth(T const& o)
{
return Depth<T>::value;
}
int main()
{
int a0;
int* a1;
int** a2;
int*** a3;
int**** a4;
int***** a5;
int****** a6;
std::cout << getDepth(a0) << std::endl;
std::cout << getDepth(a1) << std::endl;
std::cout << getDepth(a2) << std::endl;
std::cout << getDepth(a3) << std::endl;
std::cout << getDepth(a4) << std::endl;
std::cout << getDepth(a5) << std::endl;
std::cout << getDepth(a6) << std::endl;
}
Output:
> g++ test.cpp
> ./a.out
0
1
2
3
4
5
6
Going back to the reason you want it. That seems a bit suspect. Your compiler already knows what version to sue at compile time. Remember C++ is very strict on its typing and this information is known at compile time thus if you use the wrong version you will get a compiler error.

I am not aware of any built in way to do this, but you could implement a small template class that simulates a "counting pointer" in the sense that you can easily determine your "pointer depth" like this:
template <typename T> class Ptr {
private:
T* ptr;
int d;
public:
// overload all meaningful operators, like * etc.
// e.g.
T& operator*() {
return *T;
}
int depth() const {
return d;
}
}

as long as your pointer is valid you can use the following code: it'll give you refrence level to the object you pass to it (and just by a little change you can change it to return the object pointers refer to, just use comments instead of acual code and directly use countPartial):
template <class T> int countPartial(T *x,int depth)
//use template <class T> T& countPartial(T *x)
{
return countPartial(*x,depth+1);
//return countPartial(*x);
}
template <class T> int countPartial(T &x,int depth)
//use template <class T> T& countPartial(T &x)
{
return depth;
//return x;
}
template <class T> int count(T x)
{
return countPartial(x,0);
}

probably this shud solve ur trouble
T is template.
funcptr the ptr to function.
int callfunc( T ptr)
{
depth = 0;
if ( typeof(ptr)!= typeof(funcptr))
{
depth++;
depth = depth + callfunc(*ptr);
}
return depth;
}

Related

How do you specialize a member function inside a template class?

Let's say I have the following class:
template <typename T>
class SomeClass : Parent<T>
{
public:
// I have a function such as this one:
T DoSomething(const T &t)
{
return t.DoSomething(some_data);
}
// But `T` might be a pointer, so sometimes I will need something like the following
// instead (which obviously doesn't work as given):
T DoSomething(const T &t)
{
return new T(t->DoSomething(some_data));
}
private:
XYZ some_data;
};
I got stuck in a giant mess of template errors trying to implement this in any semi-nice way possible using template specialization.
In the end I came up with this very ugly solution:
template <typename T>
class SomeClass : Parent<T>
{
public:
T DoSomething(const T &x)
{
return Specializer<T>::Do(this, x);
}
private:
template <typename V>
struct Specializer {
static V Do(SomeClass *me, const V &x)
{
return x.DoSomething(me->some_data);
}
};
template <typename V>
struct Specializer<V*> {
static V* Do(SomeClass *me, const V *&x)
{
return new V(x->DoSomething(me->some_data));
}
};
XYZ some_data;
};
Is there a better way to do this that doesn't involve stuffing this function into a dummy class/struct and passing around my this pointer?
PS: In reality, this has nothing to do with pointers, but rather with different types of containers. Pointers were just an easy example to use here.
You can avoid writing any specializations, and use a type trait like std::is_pointer along with if constexpr to decide what code to execute depending on the whether the type is a pointer type or not:
auto DoSomething(const T &t)
{
if constexpr (std::is_pointer_v<T>)
return new T(t->DoSomething(some_data));
else
return t.DoSomething(some_data);
}
If you don't want to check for whether T is a pointer, but want to check something else, you can still use this pattern by dropping in a suitable replacement for is_pointer.
If you have access to c++20, you can clean up the need for any SFINAE, specializations, or if constexpr by using concepts and constraints instead. This just allows you to define the same function N times with different criteria for its insantiation, which is much more readable IMO.
This is almost the same as the SFINAE approach, but without the need for the awful syntax (no std::declval, decltype, etc). It also doesn't require all implementations to exist in one function definition like the if constexpr approach; all you need is separate function definitions with different requires clauses:
#include <concepts>
...
template <typename T>
class SomeClass : Parent<T>
{
public:
// Work for everything that's not specialized
void DoSomething(const T &t)
{
std::cout << "Basic overload" << std::endl;
}
// Only work for pointers
void DoSomething(const T& t) requires(std::is_pointer_v<T>)
{
std::cout << "Pointer overload" << std::endl;
}
// Only work if T is convertible to SomeType
void DoSomething(const T& t) requires(std::convertible_to<T, SomeType>)
{
std::cout << "Convertible to SomeType overload" << std::endl;
}
private:
XYZ some_data;
};
Live Example
In this approach there are 3 different entries:
The basic fallback for all templates
An implementation that works for any pointer type, and
An implementation that works for any T type that may be convertible to SomeType
What about using SFINAE?
For example
#include <utility>
#include <iostream>
template <typename>
struct Parent
{ };
using XYZ = int;
template <typename T>
class SomeClass : Parent<T>
{
public:
template <typename U = T>
auto DoSomething (T const & t)
-> decltype( std::declval<U>().DoSomething(std::declval<XYZ>()) )
{ std::cout << "ref\n"; return t.DoSomething(some_data); }
template <typename U = T>
auto DoSomething (T const & t)
-> std::remove_reference_t<
decltype( std::declval<U>()->DoSomething(std::declval<XYZ>()),
std::declval<T>() )>
{
using V = std::remove_reference_t<decltype(*t)>;
std::cout << "pnt\n"; return new V(t->DoSomething(some_data));
}
private:
XYZ some_data;
};
struct foo
{
foo (foo*) {}
foo () {}
foo DoSomething (int) const { return {}; }
} ;
int main()
{
SomeClass<foo> sc1;
SomeClass<foo*> sc2;
foo f;
sc1.DoSomething(f);
sc2.DoSomething(&f);
}
I mean: what about enabling the first version if, and only if, T is a type that supports a DoSomething(XYZ) method and enabling the second version if, and only if, T is a pointer of a type that supports a DoSomething(XYZ) method?

Could you please explain below code ? It compiles fine. Its related to check whether given class is base of another class [duplicate]

I want to get into more template meta-programming. I know that SFINAE stands for "substitution failure is not an error." But can someone show me a good use for SFINAE?
I like using SFINAE to check boolean conditions.
template<int I> void div(char(*)[I % 2 == 0] = 0) {
/* this is taken when I is even */
}
template<int I> void div(char(*)[I % 2 == 1] = 0) {
/* this is taken when I is odd */
}
It can be quite useful. For example, i used it to check whether an initializer list collected using operator comma is no longer than a fixed size
template<int N>
struct Vector {
template<int M>
Vector(MyInitList<M> const& i, char(*)[M <= N] = 0) { /* ... */ }
}
The list is only accepted when M is smaller than N, which means that the initializer list has not too many elements.
The syntax char(*)[C] means: Pointer to an array with element type char and size C. If C is false (0 here), then we get the invalid type char(*)[0], pointer to a zero sized array: SFINAE makes it so that the template will be ignored then.
Expressed with boost::enable_if, that looks like this
template<int N>
struct Vector {
template<int M>
Vector(MyInitList<M> const& i,
typename enable_if_c<(M <= N)>::type* = 0) { /* ... */ }
}
In practice, i often find the ability to check conditions a useful ability.
Heres one example (from here):
template<typename T>
class IsClassT {
private:
typedef char One;
typedef struct { char a[2]; } Two;
template<typename C> static One test(int C::*);
// Will be chosen if T is anything except a class.
template<typename C> static Two test(...);
public:
enum { Yes = sizeof(IsClassT<T>::test<T>(0)) == 1 };
enum { No = !Yes };
};
When IsClassT<int>::Yes is evaluated, 0 cannot be converted to int int::* because int is not a class, so it can't have a member pointer. If SFINAE didn't exist, then you would get a compiler error, something like '0 cannot be converted to member pointer for non-class type int'. Instead, it just uses the ... form which returns Two, and thus evaluates to false, int is not a class type.
In C++11 SFINAE tests have become much prettier. Here are a few examples of common uses:
Pick a function overload depending on traits
template<typename T>
std::enable_if_t<std::is_integral<T>::value> f(T t){
//integral version
}
template<typename T>
std::enable_if_t<std::is_floating_point<T>::value> f(T t){
//floating point version
}
Using a so called type sink idiom you can do pretty arbitrary tests on a type like checking if it has a member and if that member is of a certain type
//this goes in some header so you can use it everywhere
template<typename T>
struct TypeSink{
using Type = void;
};
template<typename T>
using TypeSinkT = typename TypeSink<T>::Type;
//use case
template<typename T, typename=void>
struct HasBarOfTypeInt : std::false_type{};
template<typename T>
struct HasBarOfTypeInt<T, TypeSinkT<decltype(std::declval<T&>().*(&T::bar))>> :
std::is_same<typename std::decay<decltype(std::declval<T&>().*(&T::bar))>::type,int>{};
struct S{
int bar;
};
struct K{
};
template<typename T, typename = TypeSinkT<decltype(&T::bar)>>
void print(T){
std::cout << "has bar" << std::endl;
}
void print(...){
std::cout << "no bar" << std::endl;
}
int main(){
print(S{});
print(K{});
std::cout << "bar is int: " << HasBarOfTypeInt<S>::value << std::endl;
}
Here is a live example: http://ideone.com/dHhyHE
I also recently wrote a whole section on SFINAE and tag dispatch in my blog (shameless plug but relevant) http://metaporky.blogspot.de/2014/08/part-7-static-dispatch-function.html
Note as of C++14 there is a std::void_t which is essentially the same as my TypeSink here.
Boost's enable_if library offers a nice clean interface for using SFINAE. One of my favorite usage examples is in the Boost.Iterator library. SFINAE is used to enable iterator type conversions.
Here's another (late) SFINAE example, based on Greg Rogers's answer:
template<typename T>
class IsClassT {
template<typename C> static bool test(int C::*) {return true;}
template<typename C> static bool test(...) {return false;}
public:
static bool value;
};
template<typename T>
bool IsClassT<T>::value=IsClassT<T>::test<T>(0);
In this way, you can check the value's value to see whether T is a class or not:
int main(void) {
std::cout << IsClassT<std::string>::value << std::endl; // true
std::cout << IsClassT<int>::value << std::endl; // false
return 0;
}
Examples provided by other answers seems to me more complicated than needed.
Here is the slightly easier to understand example from cppreference :
#include <iostream>
// this overload is always in the set of overloads
// ellipsis parameter has the lowest ranking for overload resolution
void test(...)
{
std::cout << "Catch-all overload called\n";
}
// this overload is added to the set of overloads if
// C is a reference-to-class type and F is a pointer to member function of C
template <class C, class F>
auto test(C c, F f) -> decltype((void)(c.*f)(), void())
{
std::cout << "Reference overload called\n";
}
// this overload is added to the set of overloads if
// C is a pointer-to-class type and F is a pointer to member function of C
template <class C, class F>
auto test(C c, F f) -> decltype((void)((c->*f)()), void())
{
std::cout << "Pointer overload called\n";
}
struct X { void f() {} };
int main(){
X x;
test( x, &X::f);
test(&x, &X::f);
test(42, 1337);
}
Output:
Reference overload called
Pointer overload called
Catch-all overload called
As you can see, in the third call of test, substitution fails without errors.
C++17 will probably provide a generic means to query for features. See N4502 for details, but as a self-contained example consider the following.
This part is the constant part, put it in a header.
// See http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4502.pdf.
template <typename...>
using void_t = void;
// Primary template handles all types not supporting the operation.
template <typename, template <typename> class, typename = void_t<>>
struct detect : std::false_type {};
// Specialization recognizes/validates only types supporting the archetype.
template <typename T, template <typename> class Op>
struct detect<T, Op, void_t<Op<T>>> : std::true_type {};
The following example, taken from N4502, shows the usage:
// Archetypal expression for assignment operation.
template <typename T>
using assign_t = decltype(std::declval<T&>() = std::declval<T const &>())
// Trait corresponding to that archetype.
template <typename T>
using is_assignable = detect<T, assign_t>;
Compared to the other implementations, this one is fairly simple: a reduced set of tools (void_t and detect) suffices. Besides, it was reported (see N4502) that it is measurably more efficient (compile-time and compiler memory consumption) than previous approaches.
Here is a live example, which includes portability tweaks for GCC pre 5.1.
Here is one good article of SFINAE: An introduction to C++'s SFINAE concept: compile-time introspection of a class member.
Summary it as following:
/*
The compiler will try this overload since it's less generic than the variadic.
T will be replace by int which gives us void f(const int& t, int::iterator* b = nullptr);
int doesn't have an iterator sub-type, but the compiler doesn't throw a bunch of errors.
It simply tries the next overload.
*/
template <typename T> void f(const T& t, typename T::iterator* it = nullptr) { }
// The sink-hole.
void f(...) { }
f(1); // Calls void f(...) { }
template<bool B, class T = void> // Default template version.
struct enable_if {}; // This struct doesn't define "type" and the substitution will fail if you try to access it.
template<class T> // A specialisation used if the expression is true.
struct enable_if<true, T> { typedef T type; }; // This struct do have a "type" and won't fail on access.
template <class T> typename enable_if<hasSerialize<T>::value, std::string>::type serialize(const T& obj)
{
return obj.serialize();
}
template <class T> typename enable_if<!hasSerialize<T>::value, std::string>::type serialize(const T& obj)
{
return to_string(obj);
}
declval is an utility that gives you a "fake reference" to an object of a type that couldn't be easily construct. declval is really handy for our SFINAE constructions.
struct Default {
int foo() const {return 1;}
};
struct NonDefault {
NonDefault(const NonDefault&) {}
int foo() const {return 1;}
};
int main()
{
decltype(Default().foo()) n1 = 1; // int n1
// decltype(NonDefault().foo()) n2 = n1; // error: no default constructor
decltype(std::declval<NonDefault>().foo()) n2 = n1; // int n2
std::cout << "n2 = " << n2 << '\n';
}
The following code uses SFINAE to let compiler select an overload based on whether a type has certain method or not:
#include <iostream>
template<typename T>
void do_something(const T& value, decltype(value.get_int()) = 0) {
std::cout << "Int: " << value.get_int() << std::endl;
}
template<typename T>
void do_something(const T& value, decltype(value.get_float()) = 0) {
std::cout << "Float: " << value.get_float() << std::endl;
}
struct FloatItem {
float get_float() const {
return 1.0f;
}
};
struct IntItem {
int get_int() const {
return -1;
}
};
struct UniversalItem : public IntItem, public FloatItem {};
int main() {
do_something(FloatItem{});
do_something(IntItem{});
// the following fails because template substitution
// leads to ambiguity
// do_something(UniversalItem{});
return 0;
}
Output:
Float: 1
Int: -1
Here, I am using template function overloading (not directly SFINAE) to determine whether a pointer is a function or member class pointer: (Is possible to fix the iostream cout/cerr member function pointers being printed as 1 or true?)
https://godbolt.org/z/c2NmzR
#include<iostream>
template<typename Return, typename... Args>
constexpr bool is_function_pointer(Return(*pointer)(Args...)) {
return true;
}
template<typename Return, typename ClassType, typename... Args>
constexpr bool is_function_pointer(Return(ClassType::*pointer)(Args...)) {
return true;
}
template<typename... Args>
constexpr bool is_function_pointer(Args...) {
return false;
}
struct test_debugger { void var() {} };
void fun_void_void(){};
void fun_void_double(double d){};
double fun_double_double(double d){return d;}
int main(void) {
int* var;
std::cout << std::boolalpha;
std::cout << "0. " << is_function_pointer(var) << std::endl;
std::cout << "1. " << is_function_pointer(fun_void_void) << std::endl;
std::cout << "2. " << is_function_pointer(fun_void_double) << std::endl;
std::cout << "3. " << is_function_pointer(fun_double_double) << std::endl;
std::cout << "4. " << is_function_pointer(&test_debugger::var) << std::endl;
return 0;
}
Prints
0. false
1. true
2. true
3. true
4. true
As the code is, it could (depending on the compiler "good" will) generate a run time call to a function which will return true or false. If you would like to force the is_function_pointer(var) to evaluate at compile type (no function calls performed at run time), you can use the constexpr variable trick:
constexpr bool ispointer = is_function_pointer(var);
std::cout << "ispointer " << ispointer << std::endl;
By the C++ standard, all constexpr variables are guaranteed to be evaluated at compile time (Computing length of a C string at compile time. Is this really a constexpr?).

How to make a C++ templated function agnostic to the return type for future specialization

I would like to have a general templated function declaration for which I do not know (already) the return type, similar to:
**template <class T> auto getIds() noexcept -> std::vector<Any>;**
The function could then be specialized with several input types, and a return type based on it:
template <> auto getIds<MyClass>() noexcept -> std::vector<decltype(MyClass::id)>
{
// implementation here.
}
And finally call it without to set the return:
auto val = getIds<MyClass>();
Is that possible? How?
Notes:
What I want to avoid is to have to set manually the Id type in the call function:
auto val = getIds<MyClass, decltype(MyClass::id)>(); // Ugly
I also discard any (non based on template) solution like extending all types from a RootMyClass. Is not that these solutions are bad, but they miss the point of this question.
Trying to be a bit clearer:
If I wrote
class MyClass { public: int id1=4;};
template <class T, class Id> auto getIds() -> Id;
template <> auto getIds<MyClass, decltype(MyClass::id1)>() -> decltype(MyClass::id1)
{
return 1;
}
auto main() -> int
{
getIds<MyClass>(); // Do not compile
getIds<MyClass, decltype(MyClass::id1)>(); // Compile but ugly
}
I would like the return type to be implicit, but I did not found a way to achieve that with specializations:
template <class T> getIds() noexcept -> WHICH TYPE?;
You cannot change the return type in a specialization, unfortunately. What you can do is change the return type in different overloads. Obviously. Furthermore, function template specializations are much more complicated than function overloads anyway, so let's do that.
Introduce an empty type wrapper, say:
template <typename T> struct wrapper { };
And forward the default implementation to that (I'm assuming C++14 here, otherwise you could wrap that in decltype() with a trailing return):
template <typename T>
auto getIds() { return getIds(wrapper<T>{}); }
Declare the generic version as:
template <typename T>
void getIds(wrapper<T> );
Don't define it. Then, anytime somebody tries to do:
auto ids = getIds<X>();
If there is no overload, that will simply fail to compile as you cannot assign from void. Then, you can overload as you see fit:
std::vector<decltype(MyClass::id)> getIds(wrapper<MyClass> )
{ ... }
FINAL EXAMPLE:
#include <iostream>
#include <vector>
template <typename T> struct wrapper { };
template <typename T>
auto getIds() -> decltype(getIds(wrapper<T>{}))
{
return getIds(wrapper<T>{});
}
template <typename T>
void getIds(wrapper<T> ) { }
struct MyClass {
int id;
};
std::vector<decltype(MyClass::id)> getIds(wrapper<MyClass> )
{
return {1, 2, 3};
}
int main()
{
for (auto id : getIds<MyClass>()) {
std::cout << id << " ";
}
}
This is actually very similar to Haskell typeclasses, and, surprisingly, works. For real usage I would use functors to allow partial specializations, though.
#include <iostream>
template<typename T>
decltype(T::x) getX(T const& t) { return; }
class A { public: int x; A(int x):x(x){} };
template<> int getX<A>(A const& a) {
return a.x;
}
class B { public: std::string x; B(std::string x):x(std::move(x)){} };
template<> std::string getX<B>(B const& b) {
return b.x;
}
int main() {
A a(42);
B b("43");
std::cout << getX(a) << std::endl;
std::cout << getX(b) << std::endl;
}
As you can see, each specialization has to (can?) provide the return type explicitly. decltype(A::x) (and B::x), respectively) could be used instead if you so prefer.
To make it even more Haskell-ish, you could expect a type tag in the type itself (basically a type family):
template<typename T>
typename T::TypeOfX getX(T const& t) { return; }
And consequently:
class A {
using TypeOfX = int;
TypeOfX someComplexLogicToGetX();
};
Both solutions to the type being instantiated for the actual type, except one gets it from a type of a field, and the other from a direct "type variable".

How to determine whether the template type is a basic type or a class

I have code something like this
template <typename T> void fun (T value)
{
.....
value.print (); //Here if T is a class I want to call print (),
//otherwise use printf
.....
}
Now, to print the value, if T is a class, I want to call the print function of the object, but if T is a basic datatype, I just want to use printf.
So, how do I find if the Template type is a basic data type or a class?
You could use std::is_class (and possibly std::is_union). The details depend on your definition of "basic type". See more on type support here.
But note that in C++ one usually overloads std::ostream& operator<<(std::ostream&, T) for printing user defined types T. This way, you do not need to worry about whether the type passed to your function template is a class or not:
template <typename T> void fun (T value)
{
std::cout << value << "\n";
}
Recommend overloading operator<<(std::ostream&) for any type T instead of using printf(): how would you know what format specifier to use?
template <typename T> void fun (T value)
{
.....
std::cout << value << std::endl;
.....
}
FWIW, std::is_class exists.
If you don't have C++11 support, an alternative.
template<typename T>
class isClassT {
private:
typedef char One;
typedef struct { char a[2]; } Two;
template<typename C> static One test(int C::*);
template<typename C> static Two test(…);
public:
enum { Yes = sizeof(isClassT<T>::test<T>(0)) == 1 };
enum { No = !Yes };
};
A simple template for finding out if type is class type. More in C++ Templates a Complete Guide.
if (isClassT<T>::Yes) {
std::cout << " Type is class " << std::endl;
}
I'd go with a printing helper function template/overload:
template <typename T>
void print(T const & t) { t.print(); }
template <typename U>
void print(U * p) { std::printf("%p", static_cast<void*>(p)); }
// we really an enable_if on is_object<U>::value here...
void print(char x) { std::printf("%c", x); }
void print(int x) { std::printf("%d", x); }
// etc. for all fundamental types
Then you can simply say print(value); in your code.

C++ replacing member variables with values from other types

I have a class T with n member variables, for example
class T{
ushort name;
ushort id;
double value;
.....etc...
};
I also have a collection of classes T1, T2...., each consisting of member variables
which are subsets of member variables of T (for lack of a better word, let me call
this a subset type of T). For example, T1 may be
class T1 {
ushort name;
double value;
};
which just picks up two members of T.
I would like to write a method
template <typename X>
T join(T t, X x)
where we return a class of type T, by replacing values of each member variable in t, with
those of x (provided X is a sub-type of T) and the other values of t remain the same.
I can think of doing this via specializations. But there should be an elegant method of doing this (perhaps detecting when a type X is a subset-type of T and doing the right thing).
I can think of doing this via specializations. But there should be an elegant method of doing this (perhaps detecting when a type X is a subset-type of T and doing the right thing).
"Detecting" is part of specialization. You should provide specializations for "subset"-types and provide no implementation for generic specialization (to provoke compilation error, when someone uses other types with that method).
If you don't want to provide specializations for all subset types. You can try to use the member detector idiom: Member Detector, or __if_exist if you are using MSVC.
So you only need to write [number of members in T] * 2 macros(and if use MSVC, you don't need the SetXXX) instead of specializing all possible combinations of members in T
CREATE_MEMBER_DETECTOR(name);
CREATE_MEMBER_DETECTOR(id);
CREATE_MEMBER_DETECTOR(value);
......
template<int N, typename T, typename R>
class SetName
{
public:
void operator()(T& t, R& r)
{
}
};
template<typename T, typename R>
class SetName<1, T, R>
{
public:
void operator()(T& t, R& r)
{
t.name = r.name;
}
};
......
(can be macros too)
And the join() should be:
template <typename SUBSET>
T join(T t, SUBSET x)
{
SetName<Detect_name<SUBSET>::value, T, SUBSET>()(t, x);
SetValue<Detect_value<SUBSET>::value, T, SUBSET>()(t, x);
......
return t;
}
I would implement conversion from T1...Tn to T (or some class that is derived from T and has info which members are actually set), and then implement join() function in terms of T. Don't think this is good place for template magic actually
Try this:
#include <iostream>
#include <typeifo>
using namespace std;
template<typename T>
struct SetBase
{
T first;
T second;
SetBase(const T& first = T(), const T& second = T())
: first(first), second(second) {}
};
template<typename T>
struct Set : public SetBase<T>
{
short name_id;
Set(const T& first = T(), const T& second = T(), const short& name) :
SetBase<T>(first, second), name_id(name){}
};
template<typename Class, typename BaseClass>
Class* join(Class **a, BaseClass *b)
{
if(typeid(Class) != typeid(BaseClass))
{
Class* retval = new Class(*static_cast<Class*>(b));
retval->name_id = (*a)->name_id;
*a = retval;
}
else
{
Class* retval = new Class(*b);
*a = retval;
}
return retval;
}
int main()
{
Set<int> *a = new Set<int>(1, 2, 3);
SetBase<int> *b = new SetBase<int>(4, 5);
cout << join(&a, b)->first << " " << join(&a, b)->second << " " << join(&a, b)->name_id << "\n";
cout << a->first << " " << a->second << " " << a->name_id << "\n";
return 0;
}
The Set class is public derived from SetBase, so the cast that I use is valid