Search code examples
c++boostc++11clang-static-analyzerc++-concepts

Why does Boost.Concept call destructors through null pointers?


When analyzing some Boost dependent code through the Clang static analyzer, I got the following error:

Logic error Called C++ object pointer is null usage.hpp 22

from the following code in boost/concept/usage.hpp

template <class Model>
struct usage_requirements
{
    ~usage_requirements() { ((Model*)0)->~Model(); }
};

Question: is this a real bug in Boost or does Boost.Concept call a destructor through a null pointer to somehow generate compiler errors during concept checking?


Solution

  • *Disclaimer. Take this with some salt, I am in no way a Boost Concept expert.

    It used in order to make the compiler to instantiate the "Model" destructor to make the compiler generate errors for concept failures.

    usage_requirements is used together with BOOST_CONCEPT_USAGE which is used while creating new concepts, see Creating Concepts in the documentation.

    #   define BOOST_CONCEPT_USAGE(model)                                    \
          model(); /* at least 2.96 and 3.4.3 both need this :( */           \
          BOOST_CONCEPT_ASSERT((boost::concepts::usage_requirements<model>)); \
          ~model()
    

    Which is used like:

    BOOST_CONCEPT_USAGE(InputIterator)
    {
        X j(i);             // require copy construction
        same_type(*i++,v);  // require postincrement-dereference returning value_type
        X& x = ++j;         // require preincrement returning X&
    }
    

    Which will end up like:

    model(); /* at least 2.96 and 3.4.3 both need this :( */           \
    BOOST_CONCEPT_ASSERT((boost::concepts::usage_requirements<model>)); \
    ~model()
    {
        X j(i);             // require copy construction
        same_type(*i++,v);  // require postincrement-dereference returning value_type
        X& x = ++j;         // require preincrement returning X&
    }
    

    As you can see, the concept requirements end up in the model destructor. Which is why we need to fool the compiler to instantiate it.