13

I'm trying to recursively dereference a pointer in C++.

If an object is passed that is not a pointer (this includes smart pointers), I just want to return the object itself, by reference if possible.

I have this code:

template<typename T> static T &dereference(T &v) { return v; }
template<typename T> static const T &dereference(const T &v) { return v; }
template<typename T> static T &dereference(T *v) { return dereference(*v); }

My code seems to work fine in most cases, but it breaks when given function pointers, because dereferencing a function pointer results in the same exact type of function pointer, causing a stack overflow.

So, how can I "stop" the dereferencing process when the dereferenced type has the same type as the original object?

Note:

I see my question has been marked as a duplicate of a similar question that uses Boost; however, I need a solution without Boost (or any other libraries).


Example:

template<typename T> T &dereference(T &v) { return v; }
template<typename T> const T &dereference(const T &v) { return v; }
template<typename T> T &dereference(T *v) { return dereference(*v); }

template<typename TCallback /* void(int) */>
void invoke(TCallback callback) { dereference(callback)(); }

void callback() { }

struct Callback {
     static void callback() { }
     void operator()() { }
};

int main() {
    Callback obj;
    invoke(Callback());          // Should work (and does)
    invoke(obj);                 // Should also work (and does)
    invoke(&obj);                // Should also work (and does)
    invoke(Callback::callback);  // Should also work (but doesn't)
    invoke(&Callback::callback); // Should also work (but doesn't)
    invoke(callback);            // Should also work (but doesn't)
    invoke(&callback);           // Should also work (but doesn't)
    return 0;
}
Community
  • 1
  • 1
user541686
  • 205,094
  • 128
  • 528
  • 886
  • 3
    possible duplicate of [Function Template Specialization on Function Pointers](http://stackoverflow.com/questions/1794640/function-template-specialization-on-function-pointers) – Lightness Races in Orbit Nov 05 '11 at 19:55
  • 1
    Could you specify exactly what compiler support you require (C++98,C++03,C++11?). – sehe Nov 05 '11 at 19:55
  • @fmaas: Sorry, I'm a little confused as to how that helps; could you elaborate a bit? – user541686 Nov 05 '11 at 20:23
  • I'd love to know what caused the downvote... – user541686 Nov 05 '11 at 20:31
  • @Mehrdad an SO community member with more than 125 reputation didn't think the question was clear or useful... On topic: this is a nice tidbit from boost [`is_function.hpp`](http://www.boost.org/doc/libs/1_47_0/boost/type_traits/is_function.hpp) _`Please note that this implementation is unnecessarily complex: we could just use !is_convertible::value, except that some compilers erroneously allow conversions from function pointers to void*`_ This could help your quest for a SFINAE trigger if MSVC9 handles it right – sehe Nov 05 '11 at 20:53
  • @sehe: Yeah I actually saw that, but I wasn't sure what to make of it... `is_convertible` also seems rather complicated too. – user541686 Nov 05 '11 at 20:58
  • IMO avoiding boost is rather complicated indeed. Especially if you're into serious template meta-programming. Then again, people who are _really_ into it, perhaps wouldn't mind writing boost from scratch :) – sehe Nov 05 '11 at 21:01
  • @sehe: Sure; if I *learn* how to write Boost from scratch, then perhaps I wouldn't mind it. But then in order to learn it, I still need to learn how this without Boost, otherwise it kind of defeats the purpose... – user541686 Nov 05 '11 at 21:46
  • @fmaas: Provided an example; thanks for the suggestion. – user541686 Nov 05 '11 at 23:31

1 Answers1

6

No dependencies at all, simple, should work on MSVC-2008.

template<typename T>
struct is_function
{
    static char     check(...);
    static double   check(const volatile void*); // function pointers are not convertible to void*
    static T        from;
    enum { value = sizeof(check(from)) != sizeof(char) };
};

template<bool, typename T = void>
struct enable_if{};

template<typename T>
struct enable_if<true, T>{typedef T type;};

template<typename T> 
T& dereference(T &v){return v;}

template<typename T> 
const T& dereference(const T& v){return v;}

template<typename T> 
typename enable_if<!is_function<T>::value, T&>::type dereference(T* v){return dereference(*v);}
ronag
  • 49,529
  • 25
  • 126
  • 221
  • 1
    `enable_if` is part of `type_traits`, you know. And it's still C++11. – Cat Plus Plus Nov 05 '11 at 21:06
  • I'm aware that enable_if is a part of type_traits in C++11. However, I have to define it myself since MSVC-2008 doesn't have it in type_traits. Are you saying that this will not work? If so, why not? SFINAE works in C++03 aswell. – ronag Nov 05 '11 at 21:12
  • TR1 was issued by the standards commitee as Technical Report 1 (ISO/IEC TR 19768). Any decent compiler implements it. There is no reason not to use it. – ronag Nov 05 '11 at 22:12
  • @ronag: If TR1 was a C++ standard, then C++11 would be largely pointless. And besides, your answer still misses the entire point of the question, which is asking how to solve the problem itself, not how to delegate it somewhere else. Furthermore, having a TR1 dependency is bad practice for code that could potentially be used with C++11 and later... not to mention that any compilers that came out before TR1 (which I guess would be "non-decent"?) would choke on this. So really, this doesn't answer the question at all. – user541686 Nov 05 '11 at 22:24
  • Your first sentence makes no sense. Why exactly is having a TR1 dependency a bad practice, C++11 have everything that TR1 has. And your compiler has TR1, if you have installed Service Pack 1, which I don't see why you wouldn't, since it fixes bugs? Personally I find your attitude rather inappropriate towards someone who wants to help, but maybe I'm just sensitive. – ronag Nov 05 '11 at 22:38
  • @ronag: Sorry if I'm a little irritated, but it's because you're making assertions like "I don't see why you wouldn't" and "there is no reason not to use it", whereas in fact those are *completely ignoring* the preface of the question, which is *specifically* for the cases in which C++11-related-features (and Boost, or the like) aren't present, which I believe I communicated quite well. Thanks for trying to help, but the answer simply isn't answering the question, as helpful as it might be in other situations. :( – user541686 Nov 05 '11 at 23:16
  • @Mehrdad: I've provided an implementation of is_function which I believe will work for you. See updated answer. – ronag Nov 05 '11 at 23:40
  • @ronag: Ah that does **exactly** what I needed! And it's really clever too, with the `sizeof` check and everything; I'll learn a lot from it. Thanks for the great answer! – user541686 Nov 05 '11 at 23:48
  • @Mehrdad: If TR1 was standard, there would still be many, many, many great reasons for C++11. TR1 is just a few extra libraries. C++11 is a bunch of awesome language features. – Puppy Nov 05 '11 at 23:52
  • @ronag: BTW, is the first function with the `enable_if>` actually necessary? Which case is it protecting against? – user541686 Nov 05 '11 at 23:57
  • If you don't have it then the function call will be ambiguous and the compiler wont know which one to choose. – ronag Nov 06 '11 at 00:00
  • @ronag: Which function call do you mean? It seemed to compile fine for me when I removed the entire function. (BTW I didn't get a reply notification since you didn't @ me.) – user541686 Nov 06 '11 at 04:10
  • @Mehrdad: I misunderstood you question. You can remove it. It got left there after all my experimentation. – ronag Nov 06 '11 at 09:01