Skip to content

Commit

Permalink
feat(Any): add checkers for holding nullptr #4447
Browse files Browse the repository at this point in the history
  • Loading branch information
aleks-f committed Feb 11, 2024
1 parent 76150f7 commit f4787b2
Show file tree
Hide file tree
Showing 4 changed files with 162 additions and 53 deletions.
60 changes: 41 additions & 19 deletions Foundation/include/Poco/Any.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,12 @@
#include <cstddef>


#define poco_any_assert(cond) do { if (!(cond)) std::abort(); } while (0)


namespace Poco {

class Any;

using namespace std::string_literals;

namespace Dynamic {

class Var;
Expand Down Expand Up @@ -407,12 +406,12 @@ ValueType* AnyCast(Any* operand)
/// to the stored value.
///
/// Example Usage:
/// MyType* pTmp = AnyCast<MyType*>(pAny).
/// Will return NULL if the cast fails, i.e. types don't match.
/// MyType* pTmp = AnyCast<MyType>(pAny).
/// Returns nullptr if the types don't match.
{
return operand && operand->type() == typeid(ValueType)
? &static_cast<Any::Holder<ValueType>*>(operand->content())->_held
: 0;
: nullptr;
}


Expand All @@ -422,8 +421,8 @@ const ValueType* AnyCast(const Any* operand)
/// to the stored value.
///
/// Example Usage:
/// const MyType* pTmp = AnyCast<MyType*>(pAny).
/// Will return NULL if the cast fails, i.e. types don't match.
/// const MyType* pTmp = AnyCast<MyType>(pAny).
/// Returns nullptr if the types don't match.
{
return AnyCast<ValueType>(const_cast<Any*>(operand));
}
Expand All @@ -440,18 +439,19 @@ ValueType AnyCast(Any& operand)
/// Some compilers will accept this code although a copy is returned. Use the RefAnyCast in
/// these cases.
{
typedef typename TypeWrapper<ValueType>::TYPE NonRef;
using NonRef = typename TypeWrapper<ValueType>::TYPE;

NonRef* result = AnyCast<NonRef>(&operand);
if (!result)
{
std::string s = "RefAnyCast: Failed to convert between Any types ";
std::string s(__func__);
s.append(": Failed to convert between Any types "s);
if (operand.content())
{
s.append(1, '(');
s.append(operand.content()->type().name());
s.append(Poco::demangle(operand.content()->type().name()));
s.append(" => ");
s.append(typeid(ValueType).name());
s.append(Poco::demangle<ValueType>());
s.append(1, ')');
}
throw BadCastException(s);
Expand All @@ -471,7 +471,7 @@ ValueType AnyCast(const Any& operand)
/// Some compilers will accept this code although a copy is returned. Use the RefAnyCast in
/// these cases.
{
typedef typename TypeWrapper<ValueType>::TYPE NonRef;
using NonRef = typename TypeWrapper<ValueType>::TYPE;

return AnyCast<NonRef&>(const_cast<Any&>(operand));
}
Expand All @@ -487,13 +487,14 @@ const ValueType& RefAnyCast(const Any & operand)
ValueType* result = AnyCast<ValueType>(const_cast<Any*>(&operand));
if (!result)
{
std::string s = "RefAnyCast: Failed to convert between Any types ";
std::string s(__func__);
s.append(": Failed to convert between Any types "s);
if (operand.content())
{
s.append(1, '(');
s.append(operand.content()->type().name());
s.append(Poco::demangle(operand.content()->type().name()));
s.append(" => ");
s.append(typeid(ValueType).name());
s.append(Poco::demangle<ValueType>());
s.append(1, ')');
}
throw BadCastException(s);
Expand All @@ -512,13 +513,14 @@ ValueType& RefAnyCast(Any& operand)
ValueType* result = AnyCast<ValueType>(&operand);
if (!result)
{
std::string s = "RefAnyCast: Failed to convert between Any types ";
std::string s(__func__);
s.append(": Failed to convert between Any types "s);
if (operand.content())
{
s.append(1, '(');
s.append(operand.content()->type().name());
s.append(Poco::demangle(operand.content()->type().name()));
s.append(" => ");
s.append(typeid(ValueType).name());
s.append(Poco::demangle<ValueType>());
s.append(1, ')');
}
throw BadCastException(s);
Expand Down Expand Up @@ -551,6 +553,26 @@ const ValueType* UnsafeAnyCast(const Any* operand)
}


template <typename ValueType>
bool AnyHoldsNullPtr(const Any& any)
/// Returns true if any holds a null pointer.
/// Fails to compile if `ValueType` is not a pointer.
{
poco_static_assert_ptr(ValueType);
return (AnyCast<ValueType>(any) == nullptr);
}


template <typename ValueType>
bool AnyHoldsNullPtr(const Any* pAny)
/// Returns true if the Any pointed to holds a null pointer.
/// Returns false if `pAny` is a null pointer.
{
if (!pAny) return false;
return (AnyHoldsNullPtr<ValueType>(*pAny));
}


} // namespace Poco


Expand Down
42 changes: 8 additions & 34 deletions Foundation/include/Poco/Bugcheck.h
Original file line number Diff line number Diff line change
Expand Up @@ -165,42 +165,16 @@ class Foundation_API Bugcheck
#endif


//
// poco_static_assert
//
// The following was ported from <boost/static_assert.hpp>
//

#define poco_static_assert(B) static_assert(B)

template <bool x>
struct POCO_STATIC_ASSERTION_FAILURE;


template <>
struct POCO_STATIC_ASSERTION_FAILURE<true>
{
enum
{
value = 1
};
};


template <int x>
struct poco_static_assert_test
{
};


#if defined(__GNUC__) && (__GNUC__ == 3) && ((__GNUC_MINOR__ == 3) || (__GNUC_MINOR__ == 4))
#define poco_static_assert(B) \
typedef char POCO_JOIN(poco_static_assert_typedef_, __LINE__) \
[POCO_STATIC_ASSERTION_FAILURE<(bool) (B)>::value]
#else
#define poco_static_assert(B) \
typedef poco_static_assert_test<sizeof(POCO_STATIC_ASSERTION_FAILURE<(bool) (B)>)> \
POCO_JOIN(poco_static_assert_typedef_, __LINE__) POCO_UNUSED
#endif
#define poco_static_assert_ptr(T) \
static_assert(std::is_pointer_v<T> || \
std::is_same_v<T, nullptr_t> || \
std::is_member_pointer_v<T> || \
std::is_member_function_pointer_v<T> || \
std::is_member_object_pointer_v<T>, \
"not a pointer")


#endif // Foundation_Bugcheck_INCLUDED
106 changes: 106 additions & 0 deletions Foundation/testsuite/src/AnyTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,111 @@ void AnyTest::testAnyInt()
}


class A
{
public:
void f() {}
int m;
};


void AnyTest::testAnyPointer()
{
int i = 13;

Any a = &i;
assertTrue (a.type() == typeid(int*));
assertFalse (AnyHoldsNullPtr<int*>(a));
assertFalse (AnyHoldsNullPtr<int*>(&a));
int* p = AnyCast<int*>(&i);
assertTrue (*p == 13);
Any b = a;
assertTrue (b.type() == typeid(int*));
int* cpyI = AnyCast<int*>(b);
assertTrue (*cpyI == *p);
*cpyI = 20;
assertTrue (*cpyI == *p);
std::string* s = AnyCast<std::string>(&a);
assertTrue (s == NULL);
assertTrue (AnyCast<nullptr_t>(&a) == nullptr);

int* POCO_UNUSED tmp = AnyCast<int*>(a);
const Any c = a;
tmp = AnyCast<int*>(a);

Any nullPtr(nullptr);
assertFalse (AnyHoldsNullPtr<nullptr_t>(nullptr));
assertFalse (AnyHoldsNullPtr<void*>(0));
assertTrue (AnyHoldsNullPtr<nullptr_t>(nullPtr));
assertTrue (AnyHoldsNullPtr<nullptr_t>(&nullPtr));
try
{
AnyHoldsNullPtr<void*>(nullPtr);
fail ("AnyCast must fail", __LINE__, __FILE__);
}
catch(const Poco::BadCastException&) {}
nullPtr = &i;
try
{
assertFalse (AnyHoldsNullPtr<nullptr_t>(nullPtr));
fail ("AnyCast must fail", __LINE__, __FILE__);
}
catch(const Poco::BadCastException&) {}
assertFalse (AnyHoldsNullPtr<int*>(nullPtr));

void* voidPtr = nullptr;
Any nullVoidPtr(voidPtr);
assertTrue (AnyHoldsNullPtr<void*>(nullVoidPtr));
try
{
AnyHoldsNullPtr<nullptr_t>(voidPtr);
fail ("AnyCast must fail", __LINE__, __FILE__);
}
catch(const Poco::BadCastException&) {}

using FP = void (AnyTest::*)();
FP fp = nullptr;
Any funcPtr(fp);
assertTrue (AnyHoldsNullPtr<FP>(funcPtr));
try
{
AnyHoldsNullPtr<FP>(voidPtr);
fail ("AnyCast must fail", __LINE__, __FILE__);
}
catch(const Poco::BadCastException&) {}
funcPtr = &AnyTest::testAnyPointer;
assertFalse (AnyHoldsNullPtr<FP>(funcPtr));

using OP = decltype(&AnyTest::_dummyObject);
OP op = nullptr;
Any objPtr(op);
assertTrue (AnyHoldsNullPtr<OP>(objPtr));
objPtr = &AnyTest::_dummyObject;
try
{
AnyHoldsNullPtr<OP>(funcPtr);
fail ("AnyCast must fail", __LINE__, __FILE__);
}
catch(const Poco::BadCastException&) {}

assertFalse (AnyHoldsNullPtr<OP>(objPtr));

using MP = decltype(&AnyTest::_dummy);
MP mp = nullptr;
Any memPtr(mp);
assertTrue (AnyHoldsNullPtr<MP>(memPtr));
memPtr = &AnyTest::_dummy;
try
{
AnyHoldsNullPtr<MP>(objPtr);
fail ("AnyCast must fail", __LINE__, __FILE__);
}
catch(const Poco::BadCastException&) {}

assertFalse (AnyHoldsNullPtr<MP>(memPtr));
}


void AnyTest::testAnyComplexType()
{
SomeClass str(13,std::string("hello"));
Expand Down Expand Up @@ -456,6 +561,7 @@ CppUnit::Test* AnyTest::suite()
CppUnit_addTest(pSuite, AnyTest, testAnySwap);
CppUnit_addTest(pSuite, AnyTest, testAnyEmptyCopy);
CppUnit_addTest(pSuite, AnyTest, testAnyCastToReference);
CppUnit_addTest(pSuite, AnyTest, testAnyPointer);
CppUnit_addTest(pSuite, AnyTest, testAnyInt);
CppUnit_addTest(pSuite, AnyTest, testAnyComplexType);
CppUnit_addTest(pSuite, AnyTest, testAnyVector);
Expand Down
7 changes: 7 additions & 0 deletions Foundation/testsuite/src/AnyTest.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
class AnyTest: public CppUnit::TestCase
{
public:
class Dummy{};

AnyTest(const std::string& name);
~AnyTest();

Expand All @@ -33,6 +35,7 @@ class AnyTest: public CppUnit::TestCase
void testAnyEmptyCopy();
void testAnyCastToReference();

void testAnyPointer();
void testAnyInt();
void testAnyComplexType();
void testAnyVector();
Expand All @@ -42,6 +45,10 @@ class AnyTest: public CppUnit::TestCase
void setUp();
void tearDown();
static CppUnit::Test* suite();

private:
int _dummy = 0;
Dummy _dummyObject;
};


Expand Down

0 comments on commit f4787b2

Please sign in to comment.