Tuesday, December 31, 2013

C++ preprocessor macros and conditional tetrary operator

I had a simple code with conditional logging:
#include <iostream>

#define LOG(condition) \
  !(condition) ? std::cout : std::cout

int main () {
  LOG(true) << "LOG(true)\n";
  LOG(false) << "LOG(false)\n";
  return 0;
}
Can be compile by below command:
g++ -o main main.cc
The output looks:
$./main
LOG(true)
$
So it misses second log message. To debug I've used command:
g++ -E -C -P -o main_pre.cc main.cc
Flags corresponds to (gcc manual):
  • -E; "Preprocess only; do not compile, assemble or link"
  • -C; "Do not discard comments. All comments are passed through to the output file, except for comments in processed directives, which are deleted along with the directive. You should be prepared for side effects when using -C; it causes the preprocessor to treat comments as tokens in their own right. For example, comments appearing at the start of what would be a directive line have the effect of turning that line into an ordinary source line, since the first token on the line is no longer a ‘#’."
  • -P; "Inhibit generation of linemarkers in the output from the preprocessor. This might be useful when running the preprocessor on something that is not C code, and will be sent to a program which might be confused by the linemarkers."
This produces file main_pre.cc which is an expanded main.cc. When you search for int main(), you will get something similar to (around line 67560):
int main() {
  !(true) ? std::cout : std::cout << "LOG(true)\n";
  !(false) ? std::cout : std::cout << "LOG(false)\n";
  return 0;
}
So, above code is equivalent to:
int main() {
  if(!(true)) { 
    std::cout;
  } else { 
    std::cout << "INFO(true)\n";
  }
  if(!(false)) {
    std::cout;
  else {
    std::cout << "LOG(false)\n";
  }
  return 0;
}

As you can see, the problem is a lack of parenthesis in macro. The correct macro:
#define LOG(condition) \
  (!(condition) ? std::cout : std::cout)
And a real life macro:
#define LOG(condition) \
  !(condition) ? 0 : std::cout
Lesson learned: macros are misleading, always put correct parenthesis.

Sunday, December 22, 2013

Cross compiling from Linux x64 to Windows XP (win32)

As a staring point, I have installed:
  • mingw-w64 binutils
  • mingw-w64 gcc
  • mingw-w64 g++
On Ubuntu 13.10 this can be done through:
sudo apt-get install binutils-mingw-w64-i686 g++-mingw-w64-i686 gcc-mingw-w64-i686
Running configure tool (e.g. when compiling 3rd party libraries):
./configure --build=x86_64-pc-linux-gnu --host=i686-mingw32 --prefix=/usr/i686-w64-mingw32
make
sudo make install
Example program:
#include <iostream>

int main(int argc, char* argv[]) {
  std::cout << "Here I am - cross compiled hello world!\n";
  return 0;
}
Compiling example program:
i686-w64-mingw32-g++ main.cc -o main.exe --static
You can test on Windows XP, it works! ;-)
Notice: If one does not specify --static flag, then a following error may happen:
main.exe: error while loading shared libraries: 
  libstdc++-6.dll: cannot open shared object file: No such file or directory
This means that a libc is not installed. There are two ways to solve it, either by compiling statically or by installing libc on Windows.

Cross compiling Boost (v1.55)


One have to prepare own user-config.jam with custom compiler i686-w64-mingw32-g++. Easiest way is to copy it from Boost package:
cp tools/build/v2/user-config.jam $HOME
echo "using gcc : 4.6 : i686-w64-mingw32-g++ ;" >> $HOME/user-config.jam
Configure and prepare build:
./bootstrap.sh mingw \
  toolset=gcc-mingw \
  target-os=windows \
  address-model=32 \
  link=shared,static \
  threading=multi \
  threadapi=win32 \
  --prefix=/usr/i686-w64-mingw32
Build and install
./b2 install \
  toolset=gcc-mingw \
  target-os=windows \
  address-model=32 \
  link=shared,static \
  threading=multi \
  threadapi=win32 \
  --prefix=/usr/i686-w64-mingw32 \
  --layout=system release
Example program using Boost.Log:
#include <boost/log/trivial.hpp>

int main(int, char*[])
{
    BOOST_LOG_TRIVIAL(trace) << "A trace severity message";
    BOOST_LOG_TRIVIAL(debug) << "A debug severity message";
    BOOST_LOG_TRIVIAL(info) << "An informational severity message";
    BOOST_LOG_TRIVIAL(warning) << "A warning severity message";
    BOOST_LOG_TRIVIAL(error) << "An error severity message";
    BOOST_LOG_TRIVIAL(fatal) << "A fatal severity message";

    return 0;
}
When compiling, one has to use -lboost_thread_w32 instead of -lboost_thread also -pthread cannot be added.
Full compilation command:
i686-w64-mingw32-g++ \
  boost_log_test.cc \
  -lboost_log \
  -lboost_log_setup \
  -lboost_thread_win32 \
  -lboost_system \
  --static \
  -o boost_log_test.exe

JsonCpp(0.6.0-rc2)


JsonCpp is easy to use C++ library to read/write/parse JSON formatted files. It can be downloaded from: http://jsoncpp.sourceforge.net/. Compilation command for cmake looks like (assuming current directory is a project dir):
mkdir build
cd build
cmake -DCMAKE_BUILD_TYPE=Release \
  -DCMAKE_TOOLCHAIN_FILE=../win32_toolchain.cmake \
  -DCMAKE_INSTALL_PREFIX=/usr/i686-w64-mingw32 \
  -DJSONCPP_WITH_TESTS=OFF \
  -DJSONCPP_WITH_POST_BUILD_UNITTEST=OFF \
  ..
sudo make install
If you wonder what win32_toolchain.cmake is, it's a Win32 cmake toolchain configuration described here: CMake documentation about cross comilation.
# the name of the target operating system
SET(CMAKE_SYSTEM_NAME Windows)

# which compilers to use for C and C++
SET(CMAKE_C_COMPILER i686-w64-mingw32-gcc)
SET(CMAKE_CXX_COMPILER i686-w64-mingw32-g++)
SET(CMAKE_RC_COMPILER i686-w64-mingw32-windres)

# here is the target environment located
SET(CMAKE_FIND_ROOT_PATH /usr/i686-w64-mingw32)

# adjust the default behaviour of the FIND_XXX() commands:
# search headers and libraries in the target environment, search 
# programs in the host environment
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
Enjoy life!