-3

I'm trying to build a pointer class with conversion safeguards in it to monitor pointer conversions. Specifically, I want it to do a dynamic_cast inside of an assert and make sure the result is not a nullptr. Unfortunately, if the pointer does not point to a class type, it gives a compile-time error indicating that dynamic_cast cannot be used.

Next I tried:

assert( (std::is_same<T,T2>() || dynamic_cast<T*>(in_ptr)) );

...but this approach still didn't work because short-circuiting only happens at run time; the same error occurred.

My next idea is to build a convert_ok struct that is specialized such that if both pointers are the same its operator() returns true, but otherwise it returns the bool result of a dynamic_cast.

I think this approach will work, but I feel like I must be missing something from the standard library. std::is_convertable comes close, but when moving from a base class to a derived class it can only know at run time if the conversion will be legal... hence the dynamic cast.

WHY: Since I know people will ask, I've built a Ptr template that reduces to a regular pointer when NDEBUG is set, but otherwise does reference counting and trips asserts if memory issues occur (reference count goes to 0 without delete, pointer accessed after deletion, pointer deleted multiple times, etc.) It has worked better for me than smart pointers because it does more bookkeeping in debug mode, while having a near-zero overhead when NDEBUG is set. It's already helped me clean up some tricky pointer juggling, and I'm now working to extend its functionality.

Charles Ofria
  • 1,936
  • 12
  • 24
  • 1
    " It has worked better for me than smart pointers" - no, it hasn't, and it won't "work better" for anyone that needs to maintain your code. –  Jun 08 '17 at 00:16
  • Perhaps I should have given more details. I'm working on high-performance software, and needed to optimize certain core areas as much as possible. I agree that it creates a higher learning curve to have a "new" type of pointer, but I'm including good documentation and making it as intuitive as possible to compensate. I can give examples and benchmarks to demonstrate the "work better", but I suspect that's not what you're looking for. – Charles Ofria Jun 08 '17 at 01:33

1 Answers1

1

Use std::enable_if in combination with a test to see if dynamic_cast is well-formed to SFINAE the right assertion. This will avoid using dynamic_cast whenever it would be ill-formed, such as when:

  • Casting to or from a non-class type, or
  • Casting from a non-polymorphic class type A to a class type B where B is not an accessible base of A.

I am using this boilerplate to implement the test.

template <typename T, typename U>
using dynamic_cast_r = decltype(dynamic_cast<U*>(static_cast<T*>(nullptr)));

template <typename T, typename U>
using can_dynamic_cast = can_apply<dynamic_cast_r, T, U>;

template <typename T>
class foo
{
public:
    foo(T *p) : p(p) { }

public:
    template <typename U>
    typename std::enable_if<can_dynamic_cast<T, U>::value, bool>::type convert_ok()
    {
        return dynamic_cast<U*>(p) != nullptr;
    }

    template <typename U>
    typename std::enable_if<!can_dynamic_cast<T, U>::value, bool>::type convert_ok()
    {
        return std::is_same<T, U>::value;
    }

private:
    T *p;
};

Demo

cdhowie
  • 158,093
  • 24
  • 286
  • 300