Different RVO behaviors between gcc and clang

Regarding RVO (Return Value Optimization), I think this video gives a real good explanation. Let’s cut to the chase. Check following code:

# cat rvo.cpp
#include <iostream>

class Foo
{
public:
        Foo(){std::cout << "Foo default constructor!\n";}
        ~Foo(){std::cout << "Foo destructor!\n";}
        Foo(const Foo&){std::cout << "Foo copy constructor!\n";}
        Foo& operator=(const Foo&){std::cout << "Foo assignment!\n"; return *this;}
        Foo(const Foo&&){std::cout << "Foo move constructor!\n";}
        Foo& operator=(const Foo&&){std::cout << "Foo move assignment!\n"; return *this;}
};

Foo func(bool flag)
{
        Foo temp;
        if (flag)
        {
                std::cout << "if\n";
        }
        else
        {
                std::cout << "else\n";
        }
        return temp;
}

int main()
{
        auto f = func(true);
        return 0;
}

On my Arch Linux platform, gcc version is 8.2.1 and clang version is 8.0.0. I tried to use -std=c++11, -std=c++14, -std=c++17 and -std=c++2a for both compilers, all generated same output:

Foo default constructor!
if
Foo destructor!

So both compilers are clever enough to realize there is no need to create “Foo temp” variable (please refer Small examples show copy elision in C++). Modify the func a little:

Foo func(bool flag)
{
        if (flag)
        {
                Foo temp;
                std::cout << "if\n";
                return temp;
        }
        else
        {
                Foo temp;
                std::cout << "else\n";
                return temp;
        }
}

This time, For clang compiler (All four compiling options: -std=c++11, -std=c++14, -std=c++17 and -std=c++2a), the program generated output as same as above:

Foo default constructor!
if
Foo destructor!

But for gcc, (All four compiling options: -std=c++11, -std=c++14, -std=c++17 and -std=c++2a), the program generated different output:

Foo default constructor!
if
Foo move constructor!
Foo destructor!
Foo destructor!

So it means gcc generated both two variables: “Foo temp” and “auto f“. It not only means clang does better optimization than gcc in this scenario, but means if you do something in move constructor, and expect it should be called. You program logic will depend on the compiler: it works under gcc but not for clang. Beware of this trap, otherwise it may bite you one day, like me today!

Modifying memory pool helps me find a hidden bug

My project has a CUDA memory pool which uses C++‘s std::queue. Allocating from the head of queue:

ptr = q.front();
q.pop(); 

While freeing memory insert it into the tail of queue:

q.push(ptr);  

I changed the implementation from std::queue to std::deque. Both allocating and freeing all occur in the front of queue:

ptr = q.front();
q.pop_front();
......
q.push_front(ptr);

This modification helps me find a hidden bug which is releasing memory early. In origin code, the memory is inserted at the end of queue. So there is a interval between it is reused by other threads and current thread, and the work can still be done correctly as long as it is not reused by others. But after using std::deque, the memory is immediately used by other threads, which disclose the bug.

 

Be wary of “Boost”, not “boost” in CMakeLists.txt

In Cygwin, the following statements in CMakeLists.txt:

......
find_package(boost 1.66 REQUIRED COMPONENTS system)
if(Boost_FOUND)
    include_directories(${Boost_INCLUDE_DIRS})
    add_executable(play main.cpp)
    target_link_libraries(play ${Boost_LIBRARIES})
endif()
......

causes following errors:

......
CMakeFiles/play.dir/main.cpp.o: In function `boost::system::error_code::error_code()':
/usr/include/boost/system/error_code.hpp:449: undefined reference to `boost::system::system_category()'
/usr/include/boost/system/error_code.hpp:449:(.text$_ZN5boost6system10error_codeC1Ev[_ZN5boost6system10error_codeC1Ev]+0x17): relocation truncated to fit: R_X86_64_PC32 against undefined symbol `boost::system::system_category()'
CMakeFiles/play.dir/main.cpp.o: In function `boost::system::error_category::std_category::equivalent(int, std::error_condition const&) const':
/usr/include/boost/system/error_code.hpp:676: undefined reference to `boost::system::generic_category()'
/usr/include/boost/system/error_code.hpp:676:(.text$_ZNK5boost6system14error_category12std_category10equivalentEiRKSt15error_condition[_ZNK5boost6system14error_category12std_category10equivalentEiRKSt15error_condition]+0xa9): relocation truncated to fit: R_X86_64_PC32 against undefined symbol `boost::system::generic_category()'
/usr/include/boost/system/error_code.hpp:679: undefined reference to `boost::system::generic_category()'
/usr/include/boost/system/error_code.hpp:679:(.text$_ZNK5boost6system14error_category12std_category10equivalentEiRKSt15error_condition[_ZNK5boost6system14error_category12std_category10equivalentEiRKSt15error_condition]+0xe4): relocation truncated to fit: R_X86_64_PC32 against undefined symbol `boost::system::generic_category()'
CMakeFiles/play.dir/main.cpp.o: In function `boost::system::error_category::std_category::equivalent(std::error_code const&, int) const':
/usr/include/boost/system/error_code.hpp:706: undefined reference to `boost::system::generic_category()'
/usr/include/boost/system/error_code.hpp:706:(.text$_ZNK5boost6system14error_category12std_category10equivalentERKSt10error_codei[_ZNK5boost6system14error_category12std_category10equivalentERKSt10error_codei]+0xab): relocation truncated to fit: R_X86_64_PC32 against undefined symbol `boost::system::generic_category()'
/usr/include/boost/system/error_code.hpp:709: undefined reference to `boost::system::generic_category()'
/usr/include/boost/system/error_code.hpp:709:(.text$_ZNK5boost6system14error_category12std_category10equivalentERKSt10error_codei[_ZNK5boost6system14error_category12std_category10equivalentERKSt10error_codei]+0xe6): relocation truncated to fit: R_X86_64_PC32 against undefined symbol `boost::system::generic_category()'
/usr/include/boost/system/error_code.hpp:721: undefined reference to `boost::system::generic_category()'
/usr/include/boost/system/error_code.hpp:721:(.text$_ZNK5boost6system14error_category12std_category10equivalentERKSt10error_codei[_ZNK5boost6system14error_category12std_category10equivalentERKSt10error_codei]+0x1be): relocation truncated to fit: R_X86_64_PC32 against undefined symbol `boost::system::generic_category()'
CMakeFiles/play.dir/main.cpp.o: In function `boost::asio::error::get_system_category()':
/usr/include/boost/asio/error.hpp:229: undefined reference to `boost::system::system_category()'
/usr/include/boost/asio/error.hpp:229:(.text$_ZN5boost4asio5error19get_system_categoryEv[_ZN5boost4asio5error19get_system_categoryEv]+0x9): relocation truncated to fit: R_X86_64_PC32 against undefined symbol `boost::system::system_category()'
collect2: error: ld returned 1 exit status

Change boost to Boost:

find_package(Boost 1.66 REQUIRED COMPONENTS system)
......

All is OK now!

Use boost on OpenBSD

Installing boost on OpenBSD is simple:

$ pkg_add boost

Write a simple test program:

#include <boost/asio.hpp>
#include <iostream>

int main()
{
    boost::system::error_code ec{};
    auto server_addr{boost::asio::ip::make_address("127.0.0.1", ec)};
    if (ec.value())
    {
        std::cerr << "Failed to parse the IP address. Error code = "
                    << ec.value() << ". Message: " << ec.message();
        return ec.value();
    }

    boost::asio::ip::tcp::endpoint ep{server_addr, 3003};
    return 0;
}

Compile it:

$ c++ client.cpp
/tmp/client-238877.o: In function `boost::asio::error::get_system_category()':
client.cpp:(.text._ZN5boost4asio5error19get_system_categoryEv[_ZN5boost4asio5error19get_system_categoryEv]+0x5): undefined reference to `boost::system::system_category()'
/tmp/client-238877.o: In function `boost::system::error_code::error_code()':
client.cpp:(.text._ZN5boost6system10error_codeC2Ev[_ZN5boost6system10error_codeC2Ev]+0x1b): undefined reference to `boost::system::system_category()'
/tmp/client-238877.o: In function `boost::system::error_category::std_category::equivalent(int, std::__1::error_condition const&) const':
client.cpp:(.text._ZNK5boost6system14error_category12std_category10equivalentEiRKNSt3__115error_conditionE[_ZNK5boost6system14error_category12std_category10equivalentEiRKNSt3__115error_conditionE]+0x129): undefined reference to `boost::system::generic_category()'
client.cpp:(.text._ZNK5boost6system14error_category12std_category10equivalentEiRKNSt3__115error_conditionE[_ZNK5boost6system14error_category12std_category10equivalentEiRKNSt3__115error_conditionE]+0x16a): undefined reference to `boost::system::generic_category()'
/tmp/client-238877.o: In function `boost::system::error_category::std_category::equivalent(std::__1::error_code const&, int) const':
client.cpp:(.text._ZNK5boost6system14error_category12std_category10equivalentERKNSt3__110error_codeEi[_ZNK5boost6system14error_category12std_category10equivalentERKNSt3__110error_codeEi]+0x137): undefined reference to `boost::system::generic_category()'
client.cpp:(.text._ZNK5boost6system14error_category12std_category10equivalentERKNSt3__110error_codeEi[_ZNK5boost6system14error_category12std_category10equivalentERKNSt3__110error_codeEi]+0x178): undefined reference to `boost::system::generic_category()'
client.cpp:(.text._ZNK5boost6system14error_category12std_category10equivalentERKNSt3__110error_codeEi[_ZNK5boost6system14error_category12std_category10equivalentERKNSt3__110error_codeEi]+0x2d2): undefined reference to `boost::system::generic_category()'
c++: error: linker command failed with exit code 1 (use -v to see invocation)

Whoops!, it means linker can’t find related library. boost libraries are installed in /usr/local/lib:

$ ls -lt /usr/local/lib/libboost*
-rw-r--r--  1 root  bin  2632774 Jul  2 05:58 /usr/local/lib/libboost_regex-mt.a
-rw-r--r--  1 root  bin  1398613 Jul  2 05:58 /usr/local/lib/libboost_regex-mt.so.8.0
-rw-r--r--  1 root  bin  2632774 Jul  2 05:58 /usr/local/lib/libboost_regex.a
-rw-r--r--  1 root  bin  1398613 Jul  2 05:58 /usr/local/lib/libboost_regex.so.8.0
-rw-r--r--  1 root  bin   994564 Jul  2 05:58 /usr/local/lib/libboost_serialization-mt.a
-rw-r--r--  1 root  bin   484918 Jul  2 05:58 /usr/local/lib/libboost_serialization-mt.so.8.0
-rw-r--r--  1 root  bin   994564 Jul  2 05:58 /usr/local/lib/libboost_serialization.a
-rw-r--r--  1 root  bin   484918 Jul  2 05:58 /usr/local/lib/libboost_serialization.so.8.0
-rw-r--r--  1 root  bin   260322 Jul  2 05:58 /usr/local/lib/libboost_signals-mt.a
-rw-r--r--  1 root  bin   154973 Jul  2 05:58 /usr/local/lib/libboost_signals-mt.so.8.0
-rw-r--r--  1 root  bin   260322 Jul  2 05:58 /usr/local/lib/libboost_signals.a
......

So I should specify boost_system library during compilation:

$ c++ -L/usr/local/lib client.cpp -lboost_system
$

This time it works!

The difference between const&constexpr objects in C++

In C++, const object means once initialized, its value can’t be changed. The initialization can occur in compile-time:

const auto c = 0;

Or in run-time:

#include <cstdlib>
#include <ctime>

auto func()
{
    return std::rand() % 10;
}

int main()
{
    std::srand(std::time(0));
    const auto c = func();

    return 0;
}

For constexpr objects, you can think they are a subset of const objects, and the constexpr object’s value must be determined in compile-time. Change the declaration of c in above code:

constexpr auto c = func();

The code can’t be compiled because the c‘s value couldn’t be generated during compilation phrase.