Generate version string at compile time

Strings can be concatenated during compilation bypassing any dynamic
memory allocation.

Change-Id: Iecd94ca44dddde4694bfeb823a0a06f174b6085b
diff --git a/internal/ceres/gradient_problem_solver.cc b/internal/ceres/gradient_problem_solver.cc
index 9382556..ad3c80e 100644
--- a/internal/ceres/gradient_problem_solver.cc
+++ b/internal/ceres/gradient_problem_solver.cc
@@ -1,5 +1,5 @@
 // Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2015 Google Inc. All rights reserved.
+// Copyright 2022 Google Inc. All rights reserved.
 // http://ceres-solver.org/
 //
 // Redistribution and use in source and binary forms, with or without
@@ -31,6 +31,7 @@
 #include "ceres/gradient_problem_solver.h"
 
 #include <memory>
+#include <string>
 
 #include "ceres/callbacks.h"
 #include "ceres/gradient_problem.h"
@@ -219,7 +220,9 @@
 string GradientProblemSolver::Summary::FullReport() const {
   using internal::VersionString;
 
-  string report = string("\nSolver Summary (v " + VersionString() + ")\n\n");
+  // NOTE operator+ is not usable for concatenating a string and a string_view.
+  string report =
+      string{"\nSolver Summary (v "}.append(VersionString()) + ")\n\n";
 
   StringAppendF(&report, "Parameters          % 25d\n", num_parameters);
   if (num_local_parameters != num_parameters) {
diff --git a/internal/ceres/solver.cc b/internal/ceres/solver.cc
index 1f60fae..47a370d 100644
--- a/internal/ceres/solver.cc
+++ b/internal/ceres/solver.cc
@@ -1,5 +1,5 @@
 // Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2015 Google Inc. All rights reserved.
+// Copyright 2022 Google Inc. All rights reserved.
 // http://ceres-solver.org/
 //
 // Redistribution and use in source and binary forms, with or without
@@ -34,6 +34,7 @@
 #include <algorithm>
 #include <memory>
 #include <sstream>  // NOLINT
+#include <string>
 #include <vector>
 
 #include "ceres/casts.h"
@@ -658,7 +659,9 @@
 string Solver::Summary::FullReport() const {
   using internal::VersionString;
 
-  string report = string("\nSolver Summary (v " + VersionString() + ")\n\n");
+  // NOTE operator+ is not usable for concatenating a string and a string_view.
+  string report =
+      string{"\nSolver Summary (v "}.append(VersionString()) + ")\n\n";
 
   StringAppendF(&report, "%45s    %21s\n", "Original", "Reduced");
   StringAppendF(&report,
diff --git a/internal/ceres/solver_utils.cc b/internal/ceres/solver_utils.cc
index 56bad63..968b958 100644
--- a/internal/ceres/solver_utils.cc
+++ b/internal/ceres/solver_utils.cc
@@ -1,5 +1,5 @@
 // Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2015 Google Inc. All rights reserved.
+// Copyright 2022 Google Inc. All rights reserved.
 // http://ceres-solver.org/
 //
 // Redistribution and use in source and binary forms, with or without
@@ -30,8 +30,6 @@
 
 #include "ceres/solver_utils.h"
 
-#include <string>
-
 #include "Eigen/Core"
 #include "ceres/internal/config.h"
 #include "ceres/internal/export.h"
@@ -49,47 +47,54 @@
   CERES_TO_STRING(EIGEN_MINOR_VERSION)
 // clang-format on
 
-std::string VersionString() {
-  std::string value = std::string(CERES_VERSION_STRING);
-  value += "-eigen-(" + std::string(CERES_EIGEN_VERSION) + ")";
+constexpr char kVersion[] =
+    // clang-format off
+  CERES_VERSION_STRING
+  "-eigen-(" CERES_EIGEN_VERSION ")"
 
 #ifdef CERES_NO_LAPACK
-  value += "-no_lapack";
+  "-no_lapack"
 #else
-  value += "-lapack";
+  "-lapack"
 #endif
 
 #ifndef CERES_NO_SUITESPARSE
-  value += "-suitesparse-(" + std::string(CERES_SUITESPARSE_VERSION) + ")";
+  "-suitesparse-(" CERES_SUITESPARSE_VERSION ")"
 #endif
 
+// TODO(sergiud) Capture METIS version
+// #ifndef CERES_NO_METIS
+//   "-metis-(" CERES_METIS_VERSION ")"
+// #endif
+
 #ifndef CERES_NO_ACCELERATE_SPARSE
-  value += "-acceleratesparse";
+  "-acceleratesparse"
 #endif
 
 #ifdef CERES_USE_EIGEN_SPARSE
-  value += "-eigensparse";
+  "-eigensparse"
 #endif
 
 #ifdef CERES_RESTRUCT_SCHUR_SPECIALIZATIONS
-  value += "-no_schur_specializations";
+  "-no_schur_specializations"
 #endif
 
 #ifdef CERES_USE_OPENMP
-  value += "-openmp";
+  "-openmp"
 #else
-  value += "-no_openmp";
+  "-no_openmp"
 #endif
 
 #ifdef CERES_NO_CUSTOM_BLAS
-  value += "-no_custom_blas";
+  "-no_custom_blas"
 #endif
 
 #ifndef CERES_NO_CUDA
-  value += "-cuda-(" + std::to_string(CUDART_VERSION) + ")";
+  "-cuda-(" CERES_TO_STRING(CUDART_VERSION) ")"
 #endif
+  ;
+// clang-format on
 
-  return value;
-}
+std::string_view VersionString() noexcept { return kVersion; }
 
 }  // namespace ceres::internal
diff --git a/internal/ceres/solver_utils.h b/internal/ceres/solver_utils.h
index 7261055..0802630 100644
--- a/internal/ceres/solver_utils.h
+++ b/internal/ceres/solver_utils.h
@@ -1,5 +1,5 @@
 // Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2015 Google Inc. All rights reserved.
+// Copyright 2022 Google Inc. All rights reserved.
 // http://ceres-solver.org/
 //
 // Redistribution and use in source and binary forms, with or without
@@ -32,7 +32,7 @@
 #define CERES_INTERNAL_SOLVER_UTILS_H_
 
 #include <algorithm>
-#include <string>
+#include <string_view>
 
 #include "ceres/internal/disable_warnings.h"
 #include "ceres/internal/export.h"
@@ -60,7 +60,7 @@
 }
 
 CERES_NO_EXPORT
-std::string VersionString();
+std::string_view VersionString() noexcept;
 
 }  // namespace ceres::internal