0

How do I detect undefined behavior in the example below?

#include <iostream>
#include <stream>
int main() {
    std::cout << "Undefined: " << std::string().front() << std::endl;
    return 0;
}

Compiled with clang++ -fsanitize=address,undefined -g -Wall -Wextra main.cc.

My expected output at either compile time or run time would be an error. Calling front() on an empty string is undefined behavior according to cplusplus.com. The actual output is Undefined:.

Questions

  1. Can I produce an error at compile or run time? If not, why can the compiler not detect this?
  2. If not, is there any tool that can detect this? For example, static analysis.

Versions used:

$ clang++ --version
Apple LLVM version 9.0.0 (clang-900.0.37)
Target: x86_64-apple-darwin17.0.0
Thread model: posix
InstalledDir: /Library/Developer/CommandLineTools/usr/bin

$ /usr/local/Cellar/gcc/7.2.0/bin/g++-7 --version
g++-7 (Homebrew GCC 7.2.0) 7.2.0
[Copyright Notice]

Related, relevant questions:

Unapiedra
  • 15,037
  • 12
  • 64
  • 93

2 Answers2

1

Turning Baum mit Augen's link into an answer.

Adding the flags -D_GLIBCXX_DEBUG -D_GLIBCXX_DEBUG_PEDANTIC when compiling with GCC works. The same does not work with Clang.

This enables a runtime check inside std::basic_string.

Results (line breaks added for clarity):

$ /usr/local/Cellar/gcc/7.2.0/bin/g++-7 main.cc -g  -D_GLIBCXX_DEBUG -D_GLIBCXX_DEBUG_PEDANTIC
$ ./a.out
Undefined:
/usr/local/Cellar/gcc/7.2.0/include/c++/7.2.0/bits/basic_string.h:1077:
 std::__cxx11::basic_string<_CharT, _Traits, _Alloc>::reference 
 std::__cxx11::basic_string<_CharT, _Traits, _Alloc>::front() 
     [with _CharT = char; _Traits = std::char_traits<char>;
      _Alloc = std::allocator<char>;
      std::__cxx11::basic_string<_CharT, _Traits, _Alloc>::reference = char&]: 
         Assertion '!empty()' failed.
[1]    24143 abort      ./a.out
Unapiedra
  • 15,037
  • 12
  • 64
  • 93
1

A Compile time warning isn't always possible with standard containers, but implementing a free function would allow extra checks which could optionally be turned off with compiler flags.

#define NDEBUG

#include <iostream>
#include <string>
#include <cassert>
#include <stdexcept>


struct assert_on_failure
{
    template<class ErrorMessage>
    void check(bool condition, ErrorMessage&& msg) const
    {
        if (!condition)
            assert(!msg);
    }
};

struct throw_on_failure
{
    template<class ErrorMessage>
    void check(bool condition, ErrorMessage&& msg) const
    {
        if (!condition)
            throw std::runtime_error(msg);
    }
};

#ifdef NDEBUG
constexpr auto default_failure_policy = throw_on_failure{};
#else
constexpr auto default_failure_policy = assert_on_failure{};
#endif



template
<
    class Container,
    class FailurePolicy = std::add_lvalue_reference_t<decltype(default_failure_policy)>
>
decltype(auto) get_front(Container&& cont, 
                         FailurePolicy&& policy = default_failure_policy)
{
    policy.check(cont.size() > 0, "container is empty");
    return cont.front();
}

int main() {
    std::cout << "Undefined: " << get_front(std::string()) << std::endl;
    return 0;
}
Richard Hodges
  • 68,278
  • 7
  • 90
  • 142
  • Thank you, @richard-hodges! This works on Clang++ too. The benefit of your solution is that it can be turned off and works for all container. Yet, my question and motivation was for the compiler (or some other tool) to detect this UB (compile time _or_ run time) -- that precludes (for me) writing/changing code where the UB is invoked. Your solution is certainly an elegant way to modify the code. It remains that I would need to change all `front` calls to `get_front`. – Unapiedra Oct 09 '17 at 13:58