Add option to use C++11 (not TR1) shared_ptr & unordered_map. - On at least some compilers, -std=c++11 is required in order to compile against std::shared_ptr & std::unordered_map, which resulted in our checks failing to find them and using the TR1 versions instead, which causes conflicts for users using C++11. - Now, if the compiler supports it and the user enables the CXX11 option, we explicitly enable C++11 before searching for shared_ptr & unordered_map, which means we should always find the C++11 versions if they are available. - As use of CXX11 results in a version of Ceres that must be used with -std=c++11 for GCC & Clang, we roll this into the Ceres target when the version of CMake supports this, otherwise we warn the user they will have to do this themselves. - CXX11 is OFF by default, to ensure that the behaviour of Ceres is unchanged from before. Change-Id: I157ea7a4fadc6bc02da176b8e771f1f327ccaf78
diff --git a/CMakeLists.txt b/CMakeLists.txt index e40dd17..1812df4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt
@@ -135,6 +135,7 @@ depends on the sparse QR factorization algorithm, which is licensed under the MPL." OFF) +OPTION(CXX11 "Enable use of C++11 if available (requires client code use C++11)." OFF) OPTION(BUILD_TESTING "Enable tests" ON) OPTION(BUILD_DOCUMENTATION "Build User's Guide (html)" OFF) OPTION(BUILD_EXAMPLES "Build examples" ON) @@ -495,53 +496,38 @@ LIST(APPEND CERES_COMPILE_OPTIONS CERES_NO_THREADS) ENDIF (OPENMP) -INCLUDE(CheckIncludeFileCXX) -CHECK_INCLUDE_FILE_CXX(unordered_map HAVE_STD_UNORDERED_MAP_HEADER) -IF (HAVE_STD_UNORDERED_MAP_HEADER) - # Finding the unordered_map header doesn't mean that unordered_map - # is in std namespace. - # - # In particular, MSVC 2008 has unordered_map declared in std::tr1. - # In order to support this, we do an extra check to see which - # namespace should be used. - INCLUDE(CheckCXXSourceCompiles) - CHECK_CXX_SOURCE_COMPILES("#include <unordered_map> - int main() { - std::unordered_map<int, int> map; - return 0; - }" - HAVE_UNORDERED_MAP_IN_STD_NAMESPACE) +# MSVC supports (in some sense) C++11, but does not use the -std=c++11 flag. +INCLUDE(CheckCXXCompilerFlag) +CHECK_CXX_COMPILER_FLAG("-std=c++11" COMPILER_HAS_CXX11_FLAG) +IF (CXX11) + # For some compilers (at least GCC 4.8), shared_ptr & unordered_map exist in + # two places, as the TR1 versions are still present. In order to avoid any + # conflicts in user code linking against Ceres which uses C++11, we enable + # C++11 (which is required when using the non-TR1 versions) if it is available + # before searching for shared_ptr & unordered_map. + IF (COMPILER_HAS_CXX11_FLAG) + # CMAKE_REQUIRED_FLAGS is used by CheckCXXSourceCompiles. + SET(CMAKE_REQUIRED_FLAGS -std=c++11) + ENDIF (COMPILER_HAS_CXX11_FLAG) +ENDIF (CXX11) + +INCLUDE(FindUnorderedMap) +FIND_UNORDERED_MAP() +IF (UNORDERED_MAP_FOUND) IF (HAVE_UNORDERED_MAP_IN_STD_NAMESPACE) LIST(APPEND CERES_COMPILE_OPTIONS CERES_STD_UNORDERED_MAP) - MESSAGE("-- Found unordered_map/set in std namespace.") - ELSE (HAVE_UNORDERED_MAP_IN_STD_NAMESPACE) - CHECK_CXX_SOURCE_COMPILES("#include <unordered_map> - int main() { - std::tr1::unordered_map<int, int> map; - return 0; - }" - HAVE_UNORDERED_MAP_IN_TR1_NAMESPACE) - IF (HAVE_UNORDERED_MAP_IN_TR1_NAMESPACE) - LIST(APPEND CERES_COMPILE_OPTIONS CERES_STD_UNORDERED_MAP_IN_TR1_NAMESPACE) - MESSAGE("-- Found unordered_map/set in std::tr1 namespace.") - ELSE (HAVE_UNORDERED_MAP_IN_TR1_NAMESPACE) - MESSAGE("-- Found <unordered_map> but cannot find either std::unordered_map " - "or std::tr1::unordered_map.") - MESSAGE("-- Replacing unordered_map/set with map/set (warning: slower!)") - LIST(APPEND CERES_COMPILE_OPTIONS CERES_NO_UNORDERED_MAP) - ENDIF (HAVE_UNORDERED_MAP_IN_TR1_NAMESPACE) - ENDIF (HAVE_UNORDERED_MAP_IN_STD_NAMESPACE) -ELSE (HAVE_STD_UNORDERED_MAP_HEADER) - CHECK_INCLUDE_FILE_CXX("tr1/unordered_map" HAVE_TR1_UNORDERED_MAP_HEADER) - IF (HAVE_TR1_UNORDERED_MAP_HEADER) + ENDIF(HAVE_UNORDERED_MAP_IN_STD_NAMESPACE) + IF (HAVE_UNORDERED_MAP_IN_TR1_NAMESPACE) + LIST(APPEND CERES_COMPILE_OPTIONS CERES_STD_UNORDERED_MAP_IN_TR1_NAMESPACE) + ENDIF(HAVE_UNORDERED_MAP_IN_TR1_NAMESPACE) + IF (HAVE_TR1_UNORDERED_MAP_IN_TR1_NAMESPACE) LIST(APPEND CERES_COMPILE_OPTIONS CERES_TR1_UNORDERED_MAP) - MESSAGE("-- Found tr1/unordered_map/set in std::tr1 namespace.") - ELSE (HAVE_TR1_UNORDERED_MAP_HEADE) - MESSAGE("-- Unable to find <unordered_map> or <tr1/unordered_map>. ") - MESSAGE("-- Replacing unordered_map/set with map/set (warning: slower!)") - LIST(APPEND CERES_COMPILE_OPTIONS CERES_NO_UNORDERED_MAP) - ENDIF (HAVE_TR1_UNORDERED_MAP_HEADER) -ENDIF (HAVE_STD_UNORDERED_MAP_HEADER) + ENDIF(HAVE_TR1_UNORDERED_MAP_IN_TR1_NAMESPACE) +ELSE (UNORDERED_MAP_FOUND) + MESSAGE("-- Replacing unordered_map/set with map/set (warning: slower!), " + "try enabling CXX11 option if you expect C++11 to be available.") + LIST(APPEND CERES_COMPILE_OPTIONS CERES_NO_UNORDERED_MAP) +ENDIF() INCLUDE(FindSharedPtr) FIND_SHARED_PTR() @@ -553,9 +539,31 @@ LIST(APPEND CERES_COMPILE_OPTIONS CERES_TR1_SHARED_PTR) ENDIF (SHARED_PTR_TR1_NAMESPACE) ELSE (SHARED_PTR_FOUND) - MESSAGE(FATAL_ERROR "Unable to find shared_ptr.") + MESSAGE(FATAL_ERROR "Unable to find shared_ptr, try enabling CXX11 option " + "if you expect C++11 to be available.") ENDIF (SHARED_PTR_FOUND) +# To ensure that CXX11 accurately captures whether we are using C++11, even on +# MSVC, check if it is required given where the potentially C++11 features Ceres +# uses were found, and disable it if C++11 is not being used. +IF (CXX11) + IF (NOT HAVE_SHARED_PTR_IN_STD_NAMESPACE AND + NOT HAVE_UNORDERED_MAP_IN_STD_NAMESPACE) + MESSAGE("-- Failed to find C++11 components in C++11 locations & " + "namespaces, disabling CXX11.") + UPDATE_CACHE_VARIABLE(CXX11 OFF) + ELSE() + MESSAGE(" ==============================================================") + MESSAGE(" Compiling Ceres using C++11. This will result in a version ") + MESSAGE(" of Ceres that will require the use of C++11 in client code.") + MESSAGE(" ==============================================================") + LIST(APPEND CERES_COMPILE_OPTIONS CERES_USE_CXX11) + IF (COMPILER_HAS_CXX11_FLAG) + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") + ENDIF() + ENDIF() +ENDIF(CXX11) + INCLUDE_DIRECTORIES( include internal
diff --git a/cmake/FindSharedPtr.cmake b/cmake/FindSharedPtr.cmake index 00d403e..3135dd5 100644 --- a/cmake/FindSharedPtr.cmake +++ b/cmake/FindSharedPtr.cmake
@@ -40,6 +40,15 @@ # otherwise it's assumed to be defined in std namespace. MACRO(FIND_SHARED_PTR) + # To support CXX11 option, clear the results of all check_xxx() functions + # s/t we always perform the checks each time, otherwise CMake fails to + # detect that the tests should be performed again after CXX11 is toggled. + UNSET(HAVE_STD_MEMORY_HEADER CACHE) + UNSET(HAVE_SHARED_PTR_IN_STD_NAMESPACE CACHE) + UNSET(HAVE_SHARED_PTR_IN_TR1_NAMESPACE CACHE) + UNSET(HAVE_TR1_MEMORY_HEADER CACHE) + UNSET(HAVE_SHARED_PTR_IN_TR1_NAMESPACE_FROM_TR1_MEMORY_HEADER CACHE) + SET(SHARED_PTR_FOUND FALSE) CHECK_INCLUDE_FILE_CXX(memory HAVE_STD_MEMORY_HEADER) IF (HAVE_STD_MEMORY_HEADER)
diff --git a/cmake/FindUnorderedMap.cmake b/cmake/FindUnorderedMap.cmake new file mode 100644 index 0000000..6aec18a --- /dev/null +++ b/cmake/FindUnorderedMap.cmake
@@ -0,0 +1,94 @@ +# Ceres Solver - A fast non-linear least squares minimizer +# Copyright 2015 Google Inc. All rights reserved. +# http://ceres-solver.org/ +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of Google Inc. nor the names of its contributors may be +# used to endorse or promote products derived from this software without +# specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# +# Author: alexs.mac@gmail.com (Alex Stewart) +# + +# FindUnorderedMap.cmake - Find unordered_map header and namespace. +# +# This module defines the following variables: +# +# UNORDERED_MAP_FOUND: TRUE if unordered_map is found. +# HAVE_UNORDERED_MAP_IN_STD_NAMESPACE: Use <unordered_map> & std. +# HAVE_UNORDERED_MAP_IN_TR1_NAMESPACE: Use <unordered_map> & std::tr1. +# HAVE_TR1_UNORDERED_MAP_IN_TR1_NAMESPACE: <tr1/unordered_map> & std::tr1. +MACRO(FIND_UNORDERED_MAP) + # To support CXX11 option, clear the results of all check_xxx() functions + # s/t we always perform the checks each time, otherwise CMake fails to + # detect that the tests should be performed again after CXX11 is toggled. + UNSET(HAVE_STD_UNORDERED_MAP_HEADER CACHE) + UNSET(HAVE_UNORDERED_MAP_IN_STD_NAMESPACE CACHE) + UNSET(HAVE_UNORDERED_MAP_IN_TR1_NAMESPACE CACHE) + UNSET(HAVE_TR1_UNORDERED_MAP_IN_TR1_NAMESPACE CACHE) + + SET(UNORDERED_MAP_FOUND FALSE) + INCLUDE(CheckIncludeFileCXX) + CHECK_INCLUDE_FILE_CXX(unordered_map HAVE_STD_UNORDERED_MAP_HEADER) + IF (HAVE_STD_UNORDERED_MAP_HEADER) + # Finding the unordered_map header doesn't mean that unordered_map + # is in std namespace. + # + # In particular, MSVC 2008 has unordered_map declared in std::tr1. + # In order to support this, we do an extra check to see which + # namespace should be used. + INCLUDE(CheckCXXSourceCompiles) + CHECK_CXX_SOURCE_COMPILES("#include <unordered_map> + int main() { + std::unordered_map<int, int> map; + return 0; + }" + HAVE_UNORDERED_MAP_IN_STD_NAMESPACE) + IF (HAVE_UNORDERED_MAP_IN_STD_NAMESPACE) + SET(UNORDERED_MAP_FOUND TRUE) + MESSAGE("-- Found unordered_map/set in std namespace.") + ELSE (HAVE_UNORDERED_MAP_IN_STD_NAMESPACE) + CHECK_CXX_SOURCE_COMPILES("#include <unordered_map> + int main() { + std::tr1::unordered_map<int, int> map; + return 0; + }" + HAVE_UNORDERED_MAP_IN_TR1_NAMESPACE) + IF (HAVE_UNORDERED_MAP_IN_TR1_NAMESPACE) + SET(UNORDERED_MAP_FOUND TRUE) + MESSAGE("-- Found unordered_map/set in std::tr1 namespace.") + ELSE (HAVE_UNORDERED_MAP_IN_TR1_NAMESPACE) + MESSAGE("-- Found <unordered_map> but cannot find either " + "std::unordered_map or std::tr1::unordered_map.") + ENDIF (HAVE_UNORDERED_MAP_IN_TR1_NAMESPACE) + ENDIF (HAVE_UNORDERED_MAP_IN_STD_NAMESPACE) + ELSE (HAVE_STD_UNORDERED_MAP_HEADER) + CHECK_INCLUDE_FILE_CXX("tr1/unordered_map" + HAVE_TR1_UNORDERED_MAP_IN_TR1_NAMESPACE) + IF (HAVE_TR1_UNORDERED_MAP_IN_TR1_NAMESPACE) + SET(UNORDERED_MAP_FOUND TRUE) + MESSAGE("-- Found tr1/unordered_map/set in std::tr1 namespace.") + ELSE (HAVE_TR1_UNORDERED_MAP_IN_TR1_NAMESPACE) + MESSAGE("-- Unable to find <unordered_map> or <tr1/unordered_map>.") + ENDIF (HAVE_TR1_UNORDERED_MAP_IN_TR1_NAMESPACE) + ENDIF (HAVE_STD_UNORDERED_MAP_HEADER) +ENDMACRO(FIND_UNORDERED_MAP)
diff --git a/cmake/config.h.in b/cmake/config.h.in index aea7921..a98fb56 100644 --- a/cmake/config.h.in +++ b/cmake/config.h.in
@@ -60,6 +60,9 @@ // routines. @CERES_NO_CUSTOM_BLAS@ +// If defined, Ceres was compiled with C++11. +@CERES_USE_CXX11@ + // If defined, Ceres was compiled without multithreading support. @CERES_NO_THREADS@ // If defined Ceres was compiled with OpenMP multithreading support.
diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index f7b58e6..fc1a70b 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt
@@ -52,6 +52,10 @@ ADD_EXECUTABLE(curve_fitting_c curve_fitting.c) TARGET_LINK_LIBRARIES(curve_fitting_c ceres) +# Force CMake to link curve_fitting_c using the C linker, this is important +# when Ceres was compiled using C++11 to ensure that -std=c++11 is not passed +# through. +SET_TARGET_PROPERTIES(curve_fitting_c PROPERTIES LINKER_LANGUAGE C) # As this is a C file #including <math.h> we have to explicitly add the math # library (libm). Although some compilers (dependent upon options) will accept # the indirect link to libm via Ceres, at least GCC 4.8 on pure Debian won't.
diff --git a/internal/ceres/CMakeLists.txt b/internal/ceres/CMakeLists.txt index f3df974..2d217c2 100644 --- a/internal/ceres/CMakeLists.txt +++ b/internal/ceres/CMakeLists.txt
@@ -184,6 +184,24 @@ VERSION ${CERES_VERSION} SOVERSION ${CERES_VERSION_MAJOR} ) +IF (CXX11 AND COMPILER_HAS_CXX11_FLAG) + IF (CMAKE_VERSION VERSION_LESS "2.8.12") + MESSAGE("-- Warning: detected CMake version: ${CMAKE_VERSION} < 2.8.12, " + "which is the minimum required for compile options to be included in an " + "exported CMake target, but CERES_USE_CXX11 is enabled and the detected. " + "compiler requires -std=c++11. The client is responsible for adding " + "-std=c++11 when using Ceres.") + ELSE () + # If Ceres is compiled using C++11, and the compiler requires -std=c++11 + # to be set, then ensure that this requirement is rolled into the exported + # CMake target, s/t client code which uses Ceres will inherit it (if the + # CMake version supports it), iff they are NOT compiling for C. We check + # for not C, rather than C++ as LINKER_LANGUAGE is often NOTFOUND and then + # uses the default (C++). + TARGET_COMPILE_OPTIONS(ceres PUBLIC + $<$<NOT:$<STREQUAL:$<TARGET_PROPERTY:LINKER_LANGUAGE>,C>>:-std=c++11>) + ENDIF() +ENDIF() IF (BUILD_SHARED_LIBS) # When building a shared library, mark all external libraries as