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