Wouldn't evaluating opaque pointer types help solve the problem?
Wouldn't evaluating opaque pointer types help solve the problem?
Oh, what they want to do is a different thing. They want to determine whether T is a complete type or not by making a metafunction. I possibly understood you wrong then. I thought that you need a mean to ensure (and only ensure) that T is complete, in which case sizeof(T) does the job (causes compile-time error). I wrapped it with static_assert so that you can use it in class scope, but obviously static_assert or such isn't needed.
Yep, sizeof works perfectly. I changed the code to:
Now I will get a compile error everytime I use it incorrectly.Code:template<typename T> struct Has_Subscribe { typedef char yes[1]; typedef char no[2]; template<typename C> static yes& test(size_t, decltype(&C::Subscribe)); template<typename C> static no& test(...); // Sanity check; if T is an incomplete type, then we cannot determine what members it has. // If T is an incomplete type, this will not compile. const static bool value = sizeof(test<T>(sizeof(T), nullptr)) == sizeof(yes); };
So thanks to everyone for their input. Another handy meta function has been created today to squash more bugs!
O_o
Didn't realize "CRTP" was also involved.
Nice catch, kmdv.
@Elysia: I realize you will not listed to me because... well because you are you, but now that you have this done, you may want to look at using normal functions and implementing your "SFINAE" primitive from that point instead of classes; several compilers have flaky support for expanding member variables from a template argument so going that route gives you a larger support surface.
Soma
Getting instances for decltype/sizeof checks via casting nullptr is unnecessarily verbose. Just use std::declval, or if that doesn't exist in your implementation, declare it yourself:
As long as you only use declval in unevaluated expressions, no need for an actual implementation to exist. Also, while *static_cast<foo*>(nullptr) is always an lvalue, with declval you can use declval<foo>() to get an rvalue and declval<foo&>() to get an lvalue.Code:template <typename T> T declval(); // no implementation // Usage: decltype(declval<foo>().something()) return_holder;
All the buzzt!
CornedBee
"There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
- Flon's Law
O_oAs long as you only use declval in unevaluated expressions, no need for an actual implementation to exist. Also, while *static_cast<foo*>(nullptr) is always an lvalue, with declval you can use declval<foo>() to get an rvalue and declval<foo&>() to get an lvalue.
I like this approach far more than using `nullptr'.
That approach is also far more likely to work on compilers that complain about using null with `static_cast'.
Soma
O_oThen do tell: how would you do it?
Before I take the effort, I'll tell you again, you'd be using normal functions.
The public interface would then be `Subscribe(sObject, sIndex)' or similar instead of `sObject.Subscribe(sIndex)'.
Though, you could of course use "CRTP" to forward from a method, but the goal of that approach is to remove the use of templates in the class interface. (So, no template classes in the ancestry nor any template methods.)
The normal function approach is also slightly more complicated to implement.
With that all in mind, do you actually want me to take the effort?
I know in the past you'd rather use `sObject.Subscribe(sIndex)' even if it made no sense, and I do not want to waste my time with an example to explain if you are going to stick to that interface in any event.
Soma
I would use whatever is less complex, less code and more portable. My requirements in order are: work on msvc, gcc, clang, less code, portable. So as long as it works on all three major compilers, I'm not going to worry that much about portability if it means vastly reduced complexity.
To be fair, the method used for detecting member functions is only something I have found on the internet. I haven't been able to do one myself that's any better.
O_oI would use whatever is less complex, less code and more portable.
The alternative I referenced is far more portable as it works with almost every compiler that correctly supports just templates functions, but if your only interested in those compiler, then you aren't really looking for what in mind.
You can change to an inheritance based query allowing you to completely forgo the type of the member in interest which removes the need for C++11 features while doing almost exactly the same thing. The major difference would be how the compiler deals with warnings and errors related to the function implementing the functionality built around the member you are interested in using. With the inheritance based solution, the template will try and use a specific form if any compatible name exists throwing an error if the form doesn't match was intended. With the type specific solution, you get a more specific version or the implementation falls back on a generic form.To be fair, the method used for detecting member functions is only something I have found on the internet. I haven't been able to do one myself that's any better.
Soma
While I care about portability, I also care about C++11 support, as you know. I love to be at the frontlines of new language features, which is why it's important for me to use compilers that support that.
As such, I cannot really be bothered by compilers that have poor C++ support. Since I am doing a lot of it just for fun, I also don't want to bother with so many compilers. I know that together, gcc and clang provide pretty good standard compliance and C++11 support, and hopefully soon, C++14 support.