Image for post
Image for post

Migrating to C++ From Other Languages

Image for post
Image for post
#include <vector>class MyActor : Actor
{
virtual void update() override
{
std::vector<std::string> strings;
strings.emplace_back("🐪 CamelCase");
strings.push_back("🐍 snake_case")
}
};

CMake

# Project Infocmake_minimum_required(VERSION 3.6 FATAL_ERROR)
cmake_policy(VERSION 3.6)
project(MyApplication
VERSION 1.0.0.0
LANGUAGES C CXX
)
# =============================================================# Common CMake Settings# Use Folders in Project Files
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
# Don't generate useless CMake Build Scripts
set(CMAKE_SUPPRESS_REGENERATION true)
# Use C++ 14
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
# Write all libraries/executables to `/bin/`
SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)
SET(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_BINARY_DIR}/bin)
SET(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE ${CMAKE_BINARY_DIR}/bin)
SET(CMAKE_LIBRARY_OUTPUT_DIRECTORY_DEBUG ${CMAKE_BINARY_DIR}/bin)
SET(CMAKE_LIBRARY_OUTPUT_DIRECTORY_RELEASE ${CMAKE_BINARY_DIR}/bin)
SET(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_DEBUG ${CMAKE_BINARY_DIR}/bin)
SET(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_RELEASE ${CMAKE_BINARY_DIR}/bin)
# Add a `d` postfix to denote debug libraries/executables
if(NOT CMAKE_DEBUG_POSTFIX)
set(CMAKE_DEBUG_POSTFIX d)
endif()
# =============================================================# Sources
file(GLOB_RECURSE FILE_SOURCES RELATIVE
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/*.h
)
# Solution Filters
foreach(source IN LISTS FILE_SOURCES)
get_filename_component(source_path "${source}" PATH)
string(REPLACE "/" "\\" source_path_msvc "${source_path}")
string(REPLACE "src" "" source_path_final "${source_path_msvc}")
source_group("${source_path_final}" FILES "${source}")
endforeach()
# =============================================================# Finalize Appadd_executable(
${PROJECT_NAME}
"${FILE_SOURCES}"
)
# =============================================================# Dependencies# For example, you could import a git submodule like so:
# add_subdirectory(external/yourdependency)
# set_property(TARGET LibraryName PROPERTY FOLDER "Dependencies")
# target_link_libraries(${PROJECT_NAME} LibraryName)
# =============================================================# Finish Settings# Write this project's libraries/executables to `/bin/`
set_target_properties(${PROJECT_NAME} PROPERTIES
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin
)
# Change working directory to top dir to access `assets` folder
if (CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
set_property(TARGET ${PROJECT_NAME} PROPERTY VS_DEBUGGER_WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/..)
set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT ${PROJECT_NAME})
endif()

Installing Dependencies

# 📂 At your project root folder
mkdir external
cd external
git submodule add https://github.com/alaingalvan/crosswindow.git
# ✖ CrossWindow
add_subdirectory(external/crosswindow)
set_property(TARGET CrossWindow PROPERTY FOLDER "Dependencies")
# 📦 Packages (installed by frameworks like Qt)
find_package(Qt5 COMPONENTS Core Gui REQUIRED)
# 📚 Libraries (such as Apple Frameworks like Cocoa, Metal, UIKit, etc.)
find_library(METAL_LIBRARY Metal)
# 👩‍💻 Cpp and Headers
add_library(Glad external/glad/src/glad.c)
target_include_directories(Glad PRIVATE external/glad/include)
include_directories(external/glad/include)
# 👒 Header Only
target_include_directories(${PROJECT_NAME} external/json/include)
target_link_libraries(${PROJECT_NAME} LibraryName)
add_dependencies(${PROJECT_NAME} LibraryName)

Building

# 👷 Make a build folder
mkdir build
cd build
# 🖼️ To build your Visual Studio solution on Windows x64
cmake .. -A x64
# 🍎 To build your XCode project On Mac OS for Mac OS
cmake .. -G Xcode
# 📱 To build your XCode project on Mac OS for iOS / iPad OS / tvOS / watchOS
cmake .. -G Xcode -DCMAKE_SYSTEM_NAME=iOS
# 🐧 To build your .make file on Linux
cmake ..
# 🌐 For WebAssembly Projects
cmake .. -DCMAKE_TOOLCHAIN_FILE="$EMSDK/emscripten/<YOUR_EMSCRIPTEN_VERSION>/cmake/Modules/Platform/Emscripten.cmake"
# 🤖 For Android write a build.gradle file# 🔨 Build on any platform:
cmake --build .
# ⚙️ Run emconfigure with the normal configure command as an argument.
$EMSDK/emscripten/emconfigure ./configure
# Run emmake with the normal make to generate linked LLVM bitcode.
$EMSDK/emscripten/emmake make
# Compile the linked bitcode generated by make (project.bc) to JavaScript.
# 'project.bc' should be replaced with the make output for your project (e.g. 'yourproject.so')
$EMSDK/emscripten/emcc project.bc -o project.js
// 🤖 To build your Android Studio project
android {
...
externalNativeBuild {
cmake {
...
// Use the following syntax when passing arguments to variables:
// arguments "-DVAR_NAME=ARGUMENT".
arguments "",
// The following line passes 'rtti' and 'exceptions' to 'ANDROID_CPP_FEATURES'.
"-DANDROID_CPP_FEATURES=rtti exceptions"
}
}
buildTypes {...}
// Use this block to link Gradle to your CMake build script.
externalNativeBuild {
cmake {...}
}
}

Code Cleanup

# Run manually to reformat a file:
# clang-format -i --style=file <file>
BasedOnStyle: llvm
DerivePointerAlignment: false
UseTab: Never
AllowShortIfStatementsOnASingleLine: true
IndentWidth: 4
TabWidth: 4
BreakBeforeBraces: Allman
AlignTrailingComments: true
AllowAllParametersOfDeclarationOnNextLine: true
Cpp11BracedListStyle: true
IndentWrappedFunctionNames: true
PointerAlignment: Left
FixNamespaceComments: false
Checks:  >
-*,
readability-*,
-readability-uppercase-literal-suffix,
-readability-magic-numbers,
-readability-isolate-declaration,
-readability-convert-member-functions-to-static,
-readability-implicit-bool-conversion,
-readability-avoid-const-params-in-decls,
-readability-simplify-boolean-expr,
-readability-make-member-function-const,
-readability-misleading-indentation, -readability-inconsistent-declaration-parameter-name,
-readability-redundant-preprocessor,
-readability-redundant-member-init,
-readability-const-return-type,
-readability-static-accessed-through-instance,
-readability-redundant-declaration,
-readability-qualified-auto,
-readability-use-anyofallof,
bugprone-*,
-bugprone-narrowing-conversions,
-bugprone-unhandled-self-assignment,
-bugprone-branch-clone,
-bugprone-macro-parentheses,
-bugprone-reserved-identifier,
-bugprone-sizeof-expression,
-bugprone-integer-division,
-bugprone-incorrect-roundings,
-bugprone-copy-constructor-init,
WarningsAsErrors: '*'

Testing

option(MYLIB_TESTS "If enabled, compile the tests." OFF)if (MYLIB_TESTS)
MESSAGE( STATUS "Building My Library Tests..." )
set_property(GLOBAL PROPERTY USE_FOLDERS ON) # we use this to get code coverage
if(CMAKE_CXX_COMPILER_ID MATCHES GNU)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fprofile-arcs -ftest-coverage")
endif()
# Unit Tests
set(GOOGLETEST_ROOT external/googletest/googletest CACHE STRING "Google Test source root")
include_directories(
${PROJECT_SOURCE_DIR}/${GOOGLETEST_ROOT}
${PROJECT_SOURCE_DIR}/${GOOGLETEST_ROOT}/include
${PROJECT_SOURCE_DIR}/src
)
set(GOOGLETEST_SOURCES
${PROJECT_SOURCE_DIR}/${GOOGLETEST_ROOT}/src/gtest-all.cc
)
foreach(_source ${GOOGLETEST_SOURCES})
set_source_files_properties(${_source} PROPERTIES GENERATED 1)
endforeach()
add_library(GoogleTest ${GOOGLETEST_SOURCES}) file(GLOB_RECURSE tests_list RELATIVE
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/tests/*.cpp
${CMAKE_CURRENT_SOURCE_DIR}/tests/*.mm
${CMAKE_CURRENT_SOURCE_DIR}/tests/*.h
${CMAKE_CURRENT_SOURCE_DIR}/tests/*.hpp
)
# Solution Filters
foreach(test IN LISTS tests_list)
get_filename_component(test_path "${test}" PATH)
string(REPLACE "/" "\\" test_path_msvc "${test_path}")
string(REPLACE "tests" "" test_path_final "${test_path_msvc}")
source_group("${test_path_final}" FILES "${test}")
endforeach()
add_executable(
Tests
"${tests_list}"
)
if(NOT MSVC)
find_package(Threads REQUIRED)
set(PThreadLib -pthread)
endif()
target_link_libraries(
Tests
${PROJECT_NAME}
${PThreadLib}
GoogleTest
)
add_dependencies(
Tests
${PROJECT_NAME}
GoogleTest
)
include(CTest)
enable_testing()
add_test(
NAME UnitTests
COMMAND Tests
)
set_property(TARGET GoogleTest PROPERTY FOLDER "Dependencies")endif()
#include "gtest/gtest.h"TEST(TestCategory, TestName)
{
int x = 0;
EXPECT_TRUE(x == x);
}
sudo: false
env:
global:
- CXX_FLAGS="-Wall -pedantic -Werror -Wno-variadic-macros -Wno-long-long -Wno-shadow"
language:
- cpp
compiler:
- g++
addons:
apt:
update: true
sources:
- ubuntu-toolchain-r-test
packages:
- lcov
- cmake
- cmake-data
before_install:
- export CXX_FLAGS=${CXX_FLAGS}" "${ENV_CXX_FLAGS}
script:
- git submodule update --init --recursive
- mkdir make
- cd make
- cmake .. -DMYAPP_TESTS=ON
- cmake --build .
- ctest . --verbose
after_success:
- lcov --directory . --capture --output-file coverage.info
- lcov --remove coverage.info '/usr/*' --output-file coverage.info
- lcov --list coverage.info
- bash <(curl -s https://codecov.io/bash) || echo "Codecov did not collect coverage reports"
notifications:
email: false
# Windows Build Configuration for AppVeyor
# http://www.appveyor.com/docs/appveyor-yml
# build version format
version: "{build}"
os: Visual Studio 2017platform:
- x64
configuration:
- Release
branches:
only:
- master
build:
parallel: true
verbosity: minimal
build_script:
- git checkout tests
- git submodule update --init --recursive
- mkdir visualstudio && cd visualstudio
- cmake .. -DMYAPP_TESTS=ON
- cmake --build .
- ctest . -C Debug --verbose
test_script:notifications:
- provider: Email
to:
- '{{commitAuthorEmail}}'
on_build_success: false
on_build_failure: false
on_build_status_changed: false
machine:
environment:
ANDROID_NDK: $HOME/android-ndk-r12b
ANDROID_NDK_HOME: $ANDROID_NDK
PATH: $PATH:$ANDROID_NDK
general:dependencies:
cache_directories:
- ~/android-ndk-r12b
pre:
- git checkout tests
- git submodule update --init --recursive
#-- Install NDK r12b --
- if [[ ! -e ~/android-ndk-r12b ]]; then wget http://dl.google.com/android/repository/android-ndk-r12b-linux-x86_64.zip && unzip -d ~ android-ndk-r12b-linux-x86_64.zip; fi
#-- Intall Android SDK Build-tools, revision 24.0.0 --
- echo y | android update sdk --no-ui --all --filter "build-tools-24.0.0"
#---Install CMake--
- wget https://github.com/Commit451/android-cmake-installer/releases/download/1.1.0/install-cmake.sh
- chmod +x install-cmake.sh
- ./install-cmake.sh
#------------------
- echo "ndk.dir=/home/ubuntu/android-ndk-r12b" > local.properties
- chmod +x gradlew
-
test:
override:
- ./gradlew assembleDebug

Conclusion

More Resources

Written by

https://Alain.xyz | Graphics Software Engineer @ Marmoset.co. Guest lecturer talking about 🛆 Computer Graphics, ✍ tech author.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store