I've read answers to this question, peeked at gcc implementation for std::any
, and didn't quite understand the implementation. However, I thought to try out the idea of using function pointers to deduce types.
In debug I get expected results, but in release my template specializations are optimized away... I've tried to use #pragma optimize("", off)
and __declspec(noinline)
but it didn't help.
AFAIK comparing pointers to functions is not UB. So I guess that MSVC (in my case) optimizes template specializations away...
Is there a way to make this work?
#include <iostream>
#include <memory>
template <typename T>
void deduce_type()
{}
class any
{
/*
template <typename T, typename ... Args>
any(Args&& ... args)
: ptr_(new T(std::forward<Args>(args) ...), std::default_delete<T>())
{}
*/
public:
template <typename T>
any(T&& other)
: ptr_(new T(std::forward<T>(other))),
deduce_type_(&deduce_type<T>)
{}
template <typename T>
bool contains_type() const
{
return deduce_type_ == &deduce_type<T>;
}
private:
// std::unique_ptr<void, std::default_delete<void>> ptr_;
void* ptr_;
void(*deduce_type_)();
};
struct Foo
{
};
int main()
{
any anyInt = 16;
any anyInt2 = 17;
any anyDouble = 2.0;
any anyFoo = Foo();
bool isInt = anyInt.contains_type<int>(),
isInt2 = anyInt2.contains_type<int>(),
notDouble = anyInt.contains_type<double>(), // 0 expected
isDouble = anyDouble.contains_type<double>(),
isFoo = anyFoo.contains_type<Foo>(),
notFoo = anyInt.contains_type<Foo>(); // 0 expected
std::cout << "is int = " << isInt << std::endl;
std::cout << "is int = " << isInt2 << std::endl;
std::cout << "is not double = " << notDouble << std::endl;
std::cout << "is double = " << isDouble << std::endl;
std::cout << "is Foo = " << isFoo << std::endl;
std::cout << "is not Foo = " << notFoo << std::endl;
return 0;
}
Release output:
is int = 1
is int = 1
is not double = 1
is double = 1
is Foo = 1
is not Foo = 1
foo is deducible = 1
Debug and expected output:
is int = 1
is int = 1
is not double = 0
is double = 1
is Foo = 1
is not Foo = 0
foo is deducible = 1
So according to the comments the problem is caused by the linker when Identical Code Folding
is enabled (by default in Release in MSVC). I've found no ways to disable this linker flags in code for separate functions, so there is nothing to do but to invent some ugly workarounds that would prevent functions from being folded to one address. That is by forcing them somehow to generate "unique" instructions for each template instantiation...
When I change the signature of a template function to simply return an integer, I get desired results, but only when particular specializations have been explicitly provided...
template <typename T>
size_t deduce_type()
{
return 0;
}
template <>
size_t deduce_type<double>()
{
return 1;
}
template <>
size_t deduce_type<Foo>()
{
return 2;
}
I get expected output in this case even if the same value is returned in different specializations. This, however, holds true only if specializations return different value then the basic template.
I guess I have to open another question...
Example 2: Passing Pointers to Functions Here, the value stored at p , *p , is 10 initially. We then passed the pointer p to the addOne() function. The ptr pointer gets this address in the addOne() function. Inside the function, we increased the value stored at ptr by 1 using (*ptr)++; .
No. One is a function pointer; the other is an object that serves as a wrapper around a function pointer. They pretty much represent the same thing, but std::function is far more powerful, allowing you to do make bindings and whatnot.
In C, we can use function pointers to avoid code redundancy. For example a simple qsort() function can be used to sort arrays in ascending order or descending or by any other order in case of array of structures. Not only this, with function pointers and void pointers, it is possible to use qsort for any data type.
It's as if we are declaring a function called *fun_ptr which takes int and returns void . The key to writing the declaration for a function pointer is to think of it as a function declaration, but with *fun_name instead of func_name . The pointer symbol * precedes the declaration of the function pointer.
You can work around this non-conformance by using a variable template (or a static local variable—in which case you call the function in the constructor—or a static data member of a class template):
template<class T>
constexpr decltype(nullptr) tag{};
class any {
void *p;
const decltype(nullptr) *t;
public:
template<class T>
any(…) : p(…),t(&tag<T>) {}
// contains_type much as before
};
This is of course basically std::type_info
, but might be preferable if the other capabilities of that facility are unneeded.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With