简体   繁体   中英

Disable BOOST_CHECK for nullptr

With Boost Test earlier than version 1.64, you cannot do this:

SomeType* a_pointer = getPointer();
BOOST_CHECK_EQUAL(a_pointer, nullptr);

This is because nullptr_t has ambiguous overloads: Boost issue #12778 and a related SO question . As in the answers to the question, this can be resolved easily:

BOOST_CHECK(!a_pointer); // rely on boolean casting, or...
// cast nullptr away from nullptr_t
BOOST_CHECK_EQUAL(a_pointer, static_cast<SomeType*>(nullptr));

However, if you support multiple boost versions, it's easy for a BOOST_CHECK_EQUAL(a_pointer, nullptr) to slip by on a newer platform, and break older ones.

One solution here is to enforce a CI platform with older Boost versions (which is useful for other reasons too, especially when the supported Boost versions extended before 1.59, when Boost Test 3 changed a lot!).

However, relying only on CI to catch this is a big delay in the OODA loop (compared to a local compiler failure) and requires network access and an simple-but-annoying VCS dance to patch the trivial changes in and resubmit the job.

Is there a way to cause it to fail to compile, even when the Boost version would otherwise support it?

In Boost Test, this is implemented in commit 229e71199 for v1.64, using the print_log_value customization point :

template<>
struct BOOST_TEST_DECL print_log_value<std::nullptr_t> {
    void operator()( std::ostream& ostr, std::nullptr_t t ) {
        ostr << "nullptr";
    }
};

It is not possible to "undef" a function defined in a different translation unit (barring some pretty nasty preprocessor hacks). So it's not really possible to "damage" the function and cause a compilation failure if you try to use the function.

However we have two better choices: use the Boost test method to avoid printing this type, or do it ourselves.


Avoid printing nullptr_t

You use the existing Boost customization point for preventing logging a type: BOOST_TEST_DONT_PRINT_LOG_VALUE :

// in your common test header
BOOST_TEST_DONT_PRINT_LOG_VALUE( std::nullptr_t )

What this does, since in Boost 1.59 is define a print_log_value function that does nothing:

#define BOOST_TEST_DONT_PRINT_LOG_VALUE( the_type )         \
namespace boost{ namespace test_tools{ namespace tt_detail{ \
template<>                                                  \
struct print_log_value<the_type > {                         \
    void    operator()( std::ostream&, the_type const& ) {} \
};                                                          \
}}}                                                         \                                                     

Before 1.59 ( commit bae8de14b ), it's defined differently (not in tt_detail for a start), but the idea is the same. This means it will work back to at least 1.58 and earlier using this macro.

However, because in 1.64, the print_log_value function was defined, if you just add the above macro, you will end up with redefinition errors from 1.64 onwards: one that does nothing from the DONT_PRINT macro, and the one that prints "nullptr" . So you can guard it with the relevant Boost version:

#if BOOST_VERSION < 106400
    BOOST_TEST_DONT_PRINT_LOG_VALUE( std::nullptr_t )
#endif

Now it will avoid printing nullptr on Boost < 1.64, and it will print on 1.64+:

[ != 0xdeadbeef]        // < 1.64, using BOOST_TEST_DONT_PRINT_LOG_VALUE
[nullptr != 0xdeadbeef] // 1.64 +, using built-in implementation

This might be good enough if you don't really care about the beauty of the logging on older Boost versions.


Do it yourself

You can also implement you own print_log_value customisation point. However, note that the namespace is different prior to 1.59, and we should only do it for <1.64, as, again, we'd be redefining the function:

// You don't need this bit if you don't support Boost Test <1.59.
#if BOOST_VERSION >= 105900
#    define BOOST_TEST_PRINT_NAMESPACE_OPEN namespace boost { namespace test_tools { namespace tt_details {
#    define BOOST_TEST_PRINT_NAMESPACE_CLOSE }}}
#else
#    define BOOST_TEST_PRINT_NAMESPACE_OPEN namespace boost { namespace test_tools {
#    define BOOST_TEST_PRINT_NAMESPACE_CLOSE }}
#endif

#if BOOST_VERSION < 106400

BOOST_TEST_PRINT_NAMESPACE_OPEN

template<>
struct print_log_value<nullptr_t> {
    inline void operator()(std::ostream& os, nullptr_t const& p) {
        os << "nullptr";
    }
};

BOOST_TEST_PRINT_NAMESPACE_CLOSE

#endif // End <1.64 condition

Now it will print the same thing:

[nullptr != 0xdeadbeef] // < 1.64, using DIY implementation
[nullptr != 0xdeadbeef] // 1.64 +, using built-in implementation

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM