summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CMake/AbseilDll.cmake1
-rw-r--r--absl/copts/GENERATED_AbseilCopts.cmake11
-rw-r--r--absl/copts/GENERATED_copts.bzl11
-rw-r--r--absl/copts/copts.py6
-rw-r--r--absl/log/BUILD.bazel1
-rw-r--r--absl/log/CMakeLists.txt1
-rw-r--r--absl/log/internal/log_message.h48
-rw-r--r--absl/log/log.h37
-rw-r--r--absl/log/log_format_test.cc131
-rw-r--r--absl/strings/BUILD.bazel1
-rw-r--r--absl/strings/CMakeLists.txt1
-rw-r--r--absl/strings/internal/has_absl_stringify.h55
-rw-r--r--absl/strings/internal/stringify_sink.h9
-rw-r--r--absl/strings/str_cat.h1
-rw-r--r--absl/synchronization/lifetime_test.cc3
15 files changed, 275 insertions, 42 deletions
diff --git a/CMake/AbseilDll.cmake b/CMake/AbseilDll.cmake
index b81b0cea..831ec5fb 100644
--- a/CMake/AbseilDll.cmake
+++ b/CMake/AbseilDll.cmake
@@ -244,6 +244,7 @@ set(ABSL_INTERNAL_DLL_FILES
"strings/internal/string_constant.h"
"strings/internal/stringify_sink.h"
"strings/internal/stringify_sink.cc"
+ "strings/internal/has_absl_stringify.h"
"strings/match.cc"
"strings/match.h"
"strings/numbers.cc"
diff --git a/absl/copts/GENERATED_AbseilCopts.cmake b/absl/copts/GENERATED_AbseilCopts.cmake
index 46563d41..ba70ef9b 100644
--- a/absl/copts/GENERATED_AbseilCopts.cmake
+++ b/absl/copts/GENERATED_AbseilCopts.cmake
@@ -13,15 +13,11 @@ list(APPEND ABSL_CLANG_CL_FLAGS
)
list(APPEND ABSL_CLANG_CL_TEST_FLAGS
- "-Wno-c99-extensions"
"-Wno-deprecated-declarations"
"-Wno-implicit-int-conversion"
- "-Wno-missing-noreturn"
"-Wno-missing-prototypes"
"-Wno-missing-variable-declarations"
- "-Wno-null-conversion"
"-Wno-shadow"
- "-Wno-shift-sign-overflow"
"-Wno-shorten-64-to-32"
"-Wno-sign-compare"
"-Wno-sign-conversion"
@@ -32,7 +28,6 @@ list(APPEND ABSL_CLANG_CL_TEST_FLAGS
"-Wno-unused-private-field"
"-Wno-unused-template"
"-Wno-used-but-marked-unused"
- "-Wno-zero-as-null-pointer-constant"
"-Wno-gnu-zero-variadic-macro-arguments"
)
@@ -55,7 +50,6 @@ list(APPEND ABSL_GCC_FLAGS
)
list(APPEND ABSL_GCC_TEST_FLAGS
- "-Wno-conversion-null"
"-Wno-deprecated-declarations"
"-Wno-missing-declarations"
"-Wno-sign-compare"
@@ -101,15 +95,11 @@ list(APPEND ABSL_LLVM_FLAGS
)
list(APPEND ABSL_LLVM_TEST_FLAGS
- "-Wno-c99-extensions"
"-Wno-deprecated-declarations"
"-Wno-implicit-int-conversion"
- "-Wno-missing-noreturn"
"-Wno-missing-prototypes"
"-Wno-missing-variable-declarations"
- "-Wno-null-conversion"
"-Wno-shadow"
- "-Wno-shift-sign-overflow"
"-Wno-shorten-64-to-32"
"-Wno-sign-compare"
"-Wno-sign-conversion"
@@ -120,7 +110,6 @@ list(APPEND ABSL_LLVM_TEST_FLAGS
"-Wno-unused-private-field"
"-Wno-unused-template"
"-Wno-used-but-marked-unused"
- "-Wno-zero-as-null-pointer-constant"
"-Wno-gnu-zero-variadic-macro-arguments"
)
diff --git a/absl/copts/GENERATED_copts.bzl b/absl/copts/GENERATED_copts.bzl
index 8518b63b..62aab656 100644
--- a/absl/copts/GENERATED_copts.bzl
+++ b/absl/copts/GENERATED_copts.bzl
@@ -14,15 +14,11 @@ ABSL_CLANG_CL_FLAGS = [
]
ABSL_CLANG_CL_TEST_FLAGS = [
- "-Wno-c99-extensions",
"-Wno-deprecated-declarations",
"-Wno-implicit-int-conversion",
- "-Wno-missing-noreturn",
"-Wno-missing-prototypes",
"-Wno-missing-variable-declarations",
- "-Wno-null-conversion",
"-Wno-shadow",
- "-Wno-shift-sign-overflow",
"-Wno-shorten-64-to-32",
"-Wno-sign-compare",
"-Wno-sign-conversion",
@@ -33,7 +29,6 @@ ABSL_CLANG_CL_TEST_FLAGS = [
"-Wno-unused-private-field",
"-Wno-unused-template",
"-Wno-used-but-marked-unused",
- "-Wno-zero-as-null-pointer-constant",
"-Wno-gnu-zero-variadic-macro-arguments",
]
@@ -56,7 +51,6 @@ ABSL_GCC_FLAGS = [
]
ABSL_GCC_TEST_FLAGS = [
- "-Wno-conversion-null",
"-Wno-deprecated-declarations",
"-Wno-missing-declarations",
"-Wno-sign-compare",
@@ -102,15 +96,11 @@ ABSL_LLVM_FLAGS = [
]
ABSL_LLVM_TEST_FLAGS = [
- "-Wno-c99-extensions",
"-Wno-deprecated-declarations",
"-Wno-implicit-int-conversion",
- "-Wno-missing-noreturn",
"-Wno-missing-prototypes",
"-Wno-missing-variable-declarations",
- "-Wno-null-conversion",
"-Wno-shadow",
- "-Wno-shift-sign-overflow",
"-Wno-shorten-64-to-32",
"-Wno-sign-compare",
"-Wno-sign-conversion",
@@ -121,7 +111,6 @@ ABSL_LLVM_TEST_FLAGS = [
"-Wno-unused-private-field",
"-Wno-unused-template",
"-Wno-used-but-marked-unused",
- "-Wno-zero-as-null-pointer-constant",
"-Wno-gnu-zero-variadic-macro-arguments",
]
diff --git a/absl/copts/copts.py b/absl/copts/copts.py
index 17c467c1..732af9ea 100644
--- a/absl/copts/copts.py
+++ b/absl/copts/copts.py
@@ -17,15 +17,11 @@ MSVC_BIG_WARNING_FLAGS = [
]
LLVM_TEST_DISABLE_WARNINGS_FLAGS = [
- "-Wno-c99-extensions",
"-Wno-deprecated-declarations",
"-Wno-implicit-int-conversion",
- "-Wno-missing-noreturn",
"-Wno-missing-prototypes",
"-Wno-missing-variable-declarations",
- "-Wno-null-conversion",
"-Wno-shadow",
- "-Wno-shift-sign-overflow",
"-Wno-shorten-64-to-32",
"-Wno-sign-compare",
"-Wno-sign-conversion",
@@ -36,7 +32,6 @@ LLVM_TEST_DISABLE_WARNINGS_FLAGS = [
"-Wno-unused-private-field",
"-Wno-unused-template",
"-Wno-used-but-marked-unused",
- "-Wno-zero-as-null-pointer-constant",
# gtest depends on this GNU extension being offered.
"-Wno-gnu-zero-variadic-macro-arguments",
]
@@ -72,7 +67,6 @@ COPT_VARS = {
"-DNOMINMAX",
],
"ABSL_GCC_TEST_FLAGS": [
- "-Wno-conversion-null",
"-Wno-deprecated-declarations",
"-Wno-missing-declarations",
"-Wno-sign-compare",
diff --git a/absl/log/BUILD.bazel b/absl/log/BUILD.bazel
index dadc8856..16788ae2 100644
--- a/absl/log/BUILD.bazel
+++ b/absl/log/BUILD.bazel
@@ -324,6 +324,7 @@ cc_test(
"//absl/log/internal:config",
"//absl/log/internal:test_matchers",
"//absl/strings",
+ "//absl/strings:str_format",
"@com_google_googletest//:gtest_main",
],
)
diff --git a/absl/log/CMakeLists.txt b/absl/log/CMakeLists.txt
index 09e4ca0c..28d4b519 100644
--- a/absl/log/CMakeLists.txt
+++ b/absl/log/CMakeLists.txt
@@ -681,6 +681,7 @@ absl_cc_test(
absl::log_internal_config
absl::log_internal_test_matchers
absl::scoped_mock_log
+ absl::str_format
absl::strings
GTest::gmock
GTest::gtest_main
diff --git a/absl/log/internal/log_message.h b/absl/log/internal/log_message.h
index 37a267c0..992bb630 100644
--- a/absl/log/internal/log_message.h
+++ b/absl/log/internal/log_message.h
@@ -41,6 +41,7 @@
#include "absl/log/internal/nullguard.h"
#include "absl/log/log_entry.h"
#include "absl/log/log_sink.h"
+#include "absl/strings/internal/has_absl_stringify.h"
#include "absl/strings/string_view.h"
#include "absl/time/time.h"
@@ -153,8 +154,17 @@ class LogMessage {
template <int SIZE>
LogMessage& operator<<(char (&buf)[SIZE]) ABSL_ATTRIBUTE_NOINLINE;
- // Default: uses `ostream` logging to convert `v` to a string.
- template <typename T>
+ // Types that support `AbslStringify()` are serialized that way.
+ template <typename T,
+ typename std::enable_if<
+ strings_internal::HasAbslStringify<T>::value, int>::type = 0>
+ LogMessage& operator<<(const T& v) ABSL_ATTRIBUTE_NOINLINE;
+
+ // Types that don't support `AbslStringify()` but do support streaming into a
+ // `std::ostream&` are serialized that way.
+ template <typename T,
+ typename std::enable_if<
+ !strings_internal::HasAbslStringify<T>::value, int>::type = 0>
LogMessage& operator<<(const T& v) ABSL_ATTRIBUTE_NOINLINE;
// Note: We explicitly do not support `operator<<` for non-const references
@@ -205,12 +215,44 @@ class LogMessage {
std::ostream stream_;
};
+// Helper class so that `AbslStringify()` can modify the LogMessage.
+class StringifySink final {
+ public:
+ explicit StringifySink(LogMessage& message) : message_(message) {}
+
+ void Append(size_t count, char ch) { message_ << std::string(count, ch); }
+
+ void Append(absl::string_view v) { message_ << v; }
+
+ // For types that implement `AbslStringify` using `absl::Format()`.
+ friend void AbslFormatFlush(StringifySink* sink, absl::string_view v) {
+ sink->Append(v);
+ }
+
+ private:
+ LogMessage& message_;
+};
+
+// Note: the following is declared `ABSL_ATTRIBUTE_NOINLINE`
+template <typename T,
+ typename std::enable_if<strings_internal::HasAbslStringify<T>::value,
+ int>::type>
+LogMessage& LogMessage::operator<<(const T& v) {
+ StringifySink sink(*this);
+ // Replace with public API.
+ AbslStringify(sink, v);
+ return *this;
+}
+
// Note: the following is declared `ABSL_ATTRIBUTE_NOINLINE`
-template <typename T>
+template <typename T,
+ typename std::enable_if<!strings_internal::HasAbslStringify<T>::value,
+ int>::type>
LogMessage& LogMessage::operator<<(const T& v) {
stream_ << log_internal::NullGuard<T>().Guard(v);
return *this;
}
+
inline LogMessage& LogMessage::operator<<(
std::ostream& (*m)(std::ostream& os)) {
stream_ << m;
diff --git a/absl/log/log.h b/absl/log/log.h
index 5331fdbf..4cd52041 100644
--- a/absl/log/log.h
+++ b/absl/log/log.h
@@ -132,10 +132,45 @@
// as they would be if streamed into a `std::ostream`, however it should be
// noted that their actual type is unspecified.
//
-// To implement a custom formatting operator for a type you own, define
+// To implement a custom formatting operator for a type you own, there are two
+// options: `AbslStringify()` or `std::ostream& operator<<(std::ostream&, ...)`.
+// It is recommended that users make their types loggable through
+// `AbslStringify()` as it is a universal stringification extension that also
+// enables `absl::StrFormat` and `absl::StrCat` support. If both
+// `AbslStringify()` and `std::ostream& operator<<(std::ostream&, ...)` are
+// defined, `AbslStringify()` will be used.
+//
+// To use the `AbslStringify()` API, define a friend function template in your
+// type's namespace with the following signature:
+//
+// template <typename Sink>
+// void AbslStringify(Sink& sink, const UserDefinedType& value);
+//
+// `Sink` has the same interface as `absl::FormatSink`, but without
+// `PutPaddedString()`.
+//
+// Example:
+//
+// struct Point {
+// template <typename Sink>
+// friend void AbslStringify(Sink& sink, const Point& p) {
+// absl::Format(&sink, "(%v, %v)", p.x, p.y);
+// }
+//
+// int x;
+// int y;
+// };
+//
+// To use `std::ostream& operator<<(std::ostream&, ...)`, define
// `std::ostream& operator<<(std::ostream&, ...)` in your type's namespace (for
// ADL) just as you would to stream it to `std::cout`.
//
+// Currently `AbslStringify()` ignores output manipulators but this is not
+// guaranteed behavior and may be subject to change in the future. If you would
+// like guaranteed behavior regarding output manipulators, please use
+// `std::ostream& operator<<(std::ostream&, ...)` to make custom types loggable
+// instead.
+//
// Those macros that support streaming honor output manipulators and `fmtflag`
// changes that output data (e.g. `std::ends`) or control formatting of data
// (e.g. `std::hex` and `std::fixed`), however flushing such a stream is
diff --git a/absl/log/log_format_test.cc b/absl/log/log_format_test.cc
index c629fce7..397c8d0c 100644
--- a/absl/log/log_format_test.cc
+++ b/absl/log/log_format_test.cc
@@ -32,6 +32,7 @@
#include "absl/log/scoped_mock_log.h"
#include "absl/strings/match.h"
#include "absl/strings/str_cat.h"
+#include "absl/strings/str_format.h"
#include "absl/strings/string_view.h"
namespace {
@@ -865,6 +866,111 @@ TEST(LogFormatTest, CustomNonCopyable) {
LOG(INFO) << value;
}
+struct Point {
+ template <typename Sink>
+ friend void AbslStringify(Sink& sink, const Point& p) {
+ absl::Format(&sink, "(%d, %d)", p.x, p.y);
+ }
+
+ int x = 10;
+ int y = 20;
+};
+
+TEST(LogFormatTest, AbslStringifyExample) {
+ absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected);
+
+ Point p;
+
+ EXPECT_CALL(
+ test_sink,
+ Send(AllOf(
+ TextMessage(Eq("(10, 20)")), TextMessage(Eq(absl::StrCat(p))),
+ ENCODED_MESSAGE(EqualsProto(R"pb(value { str: "(10, 20)" })pb")))));
+
+ test_sink.StartCapturingLogs();
+ LOG(INFO) << p;
+}
+
+struct PointWithAbslStringifiyAndOstream {
+ template <typename Sink>
+ friend void AbslStringify(Sink& sink,
+ const PointWithAbslStringifiyAndOstream& p) {
+ absl::Format(&sink, "(%d, %d)", p.x, p.y);
+ }
+
+ int x = 10;
+ int y = 20;
+};
+
+ABSL_ATTRIBUTE_UNUSED std::ostream& operator<<(
+ std::ostream& os, const PointWithAbslStringifiyAndOstream&) {
+ return os << "Default to AbslStringify()";
+}
+
+TEST(LogFormatTest, CustomWithAbslStringifyAndOstream) {
+ absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected);
+
+ PointWithAbslStringifiyAndOstream p;
+
+ EXPECT_CALL(
+ test_sink,
+ Send(AllOf(
+ TextMessage(Eq("(10, 20)")), TextMessage(Eq(absl::StrCat(p))),
+ ENCODED_MESSAGE(EqualsProto(R"pb(value { str: "(10, 20)" })pb")))));
+
+ test_sink.StartCapturingLogs();
+ LOG(INFO) << p;
+}
+
+struct PointStreamsNothing {
+ template <typename Sink>
+ friend void AbslStringify(Sink&, const PointStreamsNothing&) {}
+
+ int x = 10;
+ int y = 20;
+};
+
+TEST(LogFormatTest, AbslStringifyStreamsNothing) {
+ absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected);
+
+ PointStreamsNothing p;
+
+ EXPECT_CALL(
+ test_sink,
+ Send(AllOf(TextMessage(Eq("77")), TextMessage(Eq(absl::StrCat(p, 77))),
+ ENCODED_MESSAGE(EqualsProto(R"pb(value { str: "77" })pb")))));
+
+ test_sink.StartCapturingLogs();
+ LOG(INFO) << p << 77;
+}
+
+struct PointMultipleAppend {
+ template <typename Sink>
+ friend void AbslStringify(Sink& sink, const PointMultipleAppend& p) {
+ sink.Append("(");
+ sink.Append(absl::StrCat(p.x, ", ", p.y, ")"));
+ }
+
+ int x = 10;
+ int y = 20;
+};
+
+TEST(LogFormatTest, AbslStringifyMultipleAppend) {
+ absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected);
+
+ PointMultipleAppend p;
+
+ EXPECT_CALL(
+ test_sink,
+ Send(AllOf(
+ TextMessage(Eq("(10, 20)")), TextMessage(Eq(absl::StrCat(p))),
+ ENCODED_MESSAGE(EqualsProto(R"pb(value { str: "(" }
+ value { str: "10, 20)" })pb")))));
+
+ test_sink.StartCapturingLogs();
+ LOG(INFO) << p;
+}
+
TEST(ManipulatorLogFormatTest, BoolAlphaTrue) {
absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected);
@@ -1501,6 +1607,31 @@ TEST(ManipulatorLogFormatTest, CustomClassStreamsNothing) {
LOG(INFO) << value << 77;
}
+struct PointPercentV {
+ template <typename Sink>
+ friend void AbslStringify(Sink& sink, const PointPercentV& p) {
+ absl::Format(&sink, "(%v, %v)", p.x, p.y);
+ }
+
+ int x = 10;
+ int y = 20;
+};
+
+TEST(ManipulatorLogFormatTest, IOManipsDoNotAffectAbslStringify) {
+ absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected);
+
+ PointPercentV p;
+
+ EXPECT_CALL(
+ test_sink,
+ Send(AllOf(
+ TextMessage(Eq("(10, 20)")), TextMessage(Eq(absl::StrCat(p))),
+ ENCODED_MESSAGE(EqualsProto(R"pb(value { str: "(10, 20)" })pb")))));
+
+ test_sink.StartCapturingLogs();
+ LOG(INFO) << std::hex << p;
+}
+
// Tests that verify the behavior when more data are streamed into a `LOG`
// statement than fit in the buffer.
// Structured logging scenario is tested in other unit tests since the output is
diff --git a/absl/strings/BUILD.bazel b/absl/strings/BUILD.bazel
index 4794f4ca..12a8d155 100644
--- a/absl/strings/BUILD.bazel
+++ b/absl/strings/BUILD.bazel
@@ -58,6 +58,7 @@ cc_library(
"charconv.h",
"escaping.h",
"internal/damerau_levenshtein_distance.h",
+ "internal/has_absl_stringify.h",
"internal/string_constant.h",
"match.h",
"numbers.h",
diff --git a/absl/strings/CMakeLists.txt b/absl/strings/CMakeLists.txt
index 87b1a78f..7e91ebf2 100644
--- a/absl/strings/CMakeLists.txt
+++ b/absl/strings/CMakeLists.txt
@@ -23,6 +23,7 @@ absl_cc_library(
"escaping.h"
"internal/damerau_levenshtein_distance.h"
"internal/string_constant.h"
+ "internal/has_absl_stringify.h"
"match.h"
"numbers.h"
"str_cat.h"
diff --git a/absl/strings/internal/has_absl_stringify.h b/absl/strings/internal/has_absl_stringify.h
new file mode 100644
index 00000000..55a08508
--- /dev/null
+++ b/absl/strings/internal/has_absl_stringify.h
@@ -0,0 +1,55 @@
+// Copyright 2022 The Abseil Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef ABSL_STRINGS_INTERNAL_HAS_ABSL_STRINGIFY_H_
+#define ABSL_STRINGS_INTERNAL_HAS_ABSL_STRINGIFY_H_
+#include <string>
+#include <type_traits>
+#include <utility>
+
+#include "absl/strings/string_view.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+
+namespace strings_internal {
+
+// This is an empty class not intended to be used. It exists so that
+// `HasAbslStringify` can reference a universal class rather than needing to be
+// copied for each new sink.
+class UnimplementedSink {
+ public:
+ void Append(size_t count, char ch);
+
+ void Append(string_view v);
+
+ // Support `absl::Format(&sink, format, args...)`.
+ friend void AbslFormatFlush(UnimplementedSink* sink, absl::string_view v);
+};
+
+template <typename T, typename = void>
+struct HasAbslStringify : std::false_type {};
+
+template <typename T>
+struct HasAbslStringify<
+ T, std::enable_if_t<std::is_void<decltype(AbslStringify(
+ std::declval<strings_internal::UnimplementedSink&>(),
+ std::declval<const T&>()))>::value>> : std::true_type {};
+
+} // namespace strings_internal
+
+ABSL_NAMESPACE_END
+} // namespace absl
+
+#endif // ABSL_STRINGS_INTERNAL_HAS_ABSL_STRINGIFY_H_
diff --git a/absl/strings/internal/stringify_sink.h b/absl/strings/internal/stringify_sink.h
index 5e326a0b..fc3747bb 100644
--- a/absl/strings/internal/stringify_sink.h
+++ b/absl/strings/internal/stringify_sink.h
@@ -49,15 +49,6 @@ string_view ExtractStringification(StringifySink& sink, const T& v) {
return sink.buffer_;
}
-template <typename T, typename = void>
-struct HasAbslStringify : std::false_type {};
-
-template <typename T>
-struct HasAbslStringify<T, std::enable_if_t<std::is_void<decltype(AbslStringify(
- std::declval<strings_internal::StringifySink&>(),
- std::declval<const T&>()))>::value>>
- : std::true_type {};
-
} // namespace strings_internal
ABSL_NAMESPACE_END
diff --git a/absl/strings/str_cat.h b/absl/strings/str_cat.h
index 1a37faae..00af84ea 100644
--- a/absl/strings/str_cat.h
+++ b/absl/strings/str_cat.h
@@ -95,6 +95,7 @@
#include <vector>
#include "absl/base/port.h"
+#include "absl/strings/internal/has_absl_stringify.h"
#include "absl/strings/internal/stringify_sink.h"
#include "absl/strings/numbers.h"
#include "absl/strings/string_view.h"
diff --git a/absl/synchronization/lifetime_test.cc b/absl/synchronization/lifetime_test.cc
index 1067a331..e6274232 100644
--- a/absl/synchronization/lifetime_test.cc
+++ b/absl/synchronization/lifetime_test.cc
@@ -125,7 +125,8 @@ class OnDestruction {
// These tests require that the compiler correctly supports C++11 constant
// initialization... but MSVC has a known regression since v19.10 till v19.25:
// https://developercommunity.visualstudio.com/content/problem/336946/class-with-constexpr-constructor-not-using-static.html
-#if defined(__clang__) || !(defined(_MSC_VER) && _MSC_VER > 1900 && _MSC_VER < 1925)
+#if defined(__clang__) || \
+ !(defined(_MSC_VER) && _MSC_VER > 1900 && _MSC_VER < 1925)
// kConstInit
// Test early usage. (Declaration comes first; definitions must appear after
// the test runner.)