Search code examples
c++macosclangllvmlibc++

How to link libc++ with debug symbols?


I want to make std::optional throw exception when user asks for empty value.

#include <optional>
std::optional<int> oi;
int main(){
    *oi; // Must throw
}

c++ -std=c++17 test.cc && ./a.out

works without errors.

In clang's libc++ implementaion I found.

_LIBCPP_INLINE_VISIBILITY _LIBCPP_AVAILABILITY_BAD_OPTIONAL_ACCESS
value_type& value()
{
    if (!this->__engaged_)
        __throw_bad_optional_access();
    return this->__val_;
}
_LIBCPP_INLINE_VISIBILITY
value_type&
operator*()
{
    _LIBCPP_ASSERT(this->__engaged_, "optional operator* called for disengaged value");
    return this->__val_;
}

and then I found llvm libc++ DebugMode

c++ -std=c++2a -D_LIBCPP_DEBUG -D_LIBCPP_DEBUG_USE_EXCEPTIONS ./main.cc && ./a.out outputs

Undefined symbols for architecture x86_64:
  "std::__1::__libcpp_db::__insert_c(void*)", referenced from:
      void std::__1::__libcpp_db::__insert_c<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*) in main-eef9ea.o
  "std::__1::__libcpp_db::swap(void*, void*)", referenced from:
...

How to link libc++ with debug symbols?

P.S.

 c++ --version
Apple LLVM version 10.0.1 (clang-1001.0.46.4)
Target: x86_64-apple-darwin18.7.0
Thread model: posix
InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin

Solution

  • Portable workaround.

    #include <optional>
    
    // The same as std::optional, but 
    // throw and exception when ask for empty value.
    // Use assert(0) when exceptions disabled.
    template<typename T_>
    struct Result : std::optional<T_>{
       using T = T_;
       using parent = std::optional<T_>;
       using parent::parent;
       using typename parent::value_type;
       using parent::operator bool;
    
       constexpr
       const value_type&
       operator*() const
       {
          ensure_is_engaged();
          return this->value();
       }
    
       value_type&
       operator*()
       {
          ensure_is_engaged();
          return this->value();
       }
    
    private:
       void ensure_is_engaged(){
          if( bool is_disengaged = not bool(*this) )
           #if __EXCEPTIONS
             throw std::runtime_error("failed to get disengaged value from std::optional");
           #else
             assert(0 && "failed to get disengaged value from std::optional");
           #endif
       }
    };
    

    Test

    int main()
    {
       using std::cout, std::cerr, std::endl;
       Result<int> normal=1, fail;
       cout<<*normal<<*fail;
    }
    

    c++ -std=c++17 -fno-exceptions test.cc && ./a.out

    1
    Assertion failed: (0 && "failed to get disengaged value from std::optional"), function ensure_is_engaged, file ./result.hh, line 38.
    fish: './a.out' terminated by signal SIGABRT (Abort)
    

    c++ -std=c++17 test.cc && ./a.out assume default -fexceptions

    1
    libc++abi.dylib: terminating with uncaught exception of type std::runtime_error: failed to get disengaged value from std::optional
    fish: './a.out' terminated by signal SIGABRT (Abort)