From d0cd94f392b0f43ca1bbbbf30bffbcdad3e0aa5b Mon Sep 17 00:00:00 2001 From: Carlos O'Ryan Date: Fri, 18 Dec 2020 15:50:15 -0500 Subject: [PATCH 1/9] doc: fix formatting for code samples (#462) --- docs/cpp-metrics-api-design.md | 68 +++++++++++++++++------------ docs/cpp-metrics-sdk-design.md | 23 +++++----- docs/cpp-ostream-exporter-design.md | 4 +- 3 files changed, 53 insertions(+), 42 deletions(-) diff --git a/docs/cpp-metrics-api-design.md b/docs/cpp-metrics-api-design.md index 8279bfb4a3..cdfdbdecd0 100644 --- a/docs/cpp-metrics-api-design.md +++ b/docs/cpp-metrics-api-design.md @@ -49,7 +49,7 @@ A `MeterProvider` interface must support a `global.SetMeterProvider(MeterProvid * name (required): identifies the instrumentation library. * version (optional): specifies the version of the instrumenting library (the library injecting OpenTelemetry calls into the code) -``` +```cc # meter_provider.h class Provider { @@ -94,7 +94,7 @@ private: -``` +```cc # meter_provider.h class MeterProvider { @@ -126,9 +126,9 @@ This interface consists of a set of **instrument constructors**, and a **facilit -``` +```cc # meter.h -Class Meter { +class Meter { public: /////////////////////////Metric Instrument Constructors//////////////////////////// @@ -184,7 +184,7 @@ public: * of labels with a single API call. Implementations should find bound metric * instruments that match the key-value pairs in the labels. * - * Arugments: + * Arguments: * labels, labels associated with all measurements in the batch. * instruments, a span of pointers to instruments to record to. * values, a synchronized span of values to record to those instruments. @@ -275,7 +275,7 @@ Direct calling must also be supported. The user can specify labels with the cap MUST support `RecordBatch` calling (where a single set of labels is applied to several metric instruments). -``` +```cc # metric.h /* @@ -340,15 +340,16 @@ public: -``` +```cc template class SynchronousInstrument: public Instrument { public: SynchronousInstrument() = default; - SynchronousInstrument(nostd::string_view name, nostd::string_view description, nostd::string_view unit, bool enabled); + SynchronousInstrument(nostd::string_view name, nostd::string_view description, nostd::string_view unit, + bool enabled); - /** + /** * Returns a Bound Instrument associated with the specified labels. * Multiples requests with the same set of labels may return the same * Bound Instrument instance. @@ -358,10 +359,10 @@ public: * * @param labels the set of labels, as key-value pairs. * @return a Bound Instrument - */ + */ BoundSynchronousInstrument bind(nostd::KeyValueIterable labels); - /** + /** * Records a single synchronous metric event. * Since this is an unbound synchronous instrument, labels are required in * metric capture calls. * @@ -369,34 +370,36 @@ public: * @param labels the set of labels, as key-value pairs. * @param value the numerical representation of the metric being captured * @return void - */ + */ void update(T value, nostd::KeyValueIterable labels); //add or record }; + template class BoundSynchronousInstrument: public Instrument { public: BoundSynchronousInstrument() = default; // Will also call the processor to acquire the correct aggregator for this instrument - BoundSynchronousInstrument(nostd::string_view name, nostd::string_view description, nostd::string_view unit, bool enabled); + BoundSynchronousInstrument(nostd::string_view name, nostd::string_view description, nostd::string_view unit, + bool enabled); - /** + /** * Frees the resources associated with this Bound Instrument. * The Metric from which this instrument was created is not impacted. * * @param none * @return void - */ + */ void unbind(); - /** + /** * Records a single synchronous metric event. //Call to aggregator * Since this is a bound synchronous instrument, labels are notrequired in * metric capture calls. * * @param value the numerical representation of the metric being captured * @return void - */ + */ void update(T value); //add or record }; @@ -404,7 +407,8 @@ public: template class AsynchronousInstrument: public Instrument{ public: - AsynchronousInstrument(nostd::string_view name, nostd::string_view description, nostd::string_view unit, bool enabled, void (*callback)(ObserverResult)); + AsynchronousInstrument(nostd::string_view name, nostd::string_view description, nostd::string_view unit, + bool enabled, void (*callback)(ObserverResult)); /** * Captures data by activating the callback function associated with the @@ -416,7 +420,7 @@ public: */ void observe(); - /** + /** * Captures data from the stored callback function. The callback itself * makes use of the instrument's observe function to take capture * responsibilities out of the user's hands. @@ -439,14 +443,16 @@ private: The Counter below is an example of one Metric instrument. It is important to note that in the Counter’s add function, it binds the labels to the instrument before calling add, then unbinds. Therefore all interactions with the aggregator take place through bound instruments and by extension, the BaseBoundInstrument Class. -``` +```cc template class BoundCounter: public BoundSynchronousInstrument{ //override bind? public: BoundCounter() = default; BoundCounter(nostd::string_view name, nostd::string_view description, nostd::string_view unit, bool enabled); - /* - * Add adds the value to the counter's sum. The labels are already linked * to the instrument and are not specified. + + /* + * Add adds the value to the counter's sum. The labels are already linked + * to the instrument and are not specified. * * @param value the numerical representation of the metric being captured * @param labels the set of labels, as key-value pairs @@ -455,23 +461,26 @@ public: void unbind(); }; + template class Counter: public SynchronousInstrument{ public: Counter() = default; Counter(nostd::string_view name, nostd::string_view description, nostd::string_view unit, bool enabled); - /* + + /* * Bind creates a bound instrument for this counter. The labels are * associated with values recorded via subsequent calls to Record. * * @param labels the set of labels, as key-value pairs. * @return a BoundIntCounter tied to the specified labels */ - BoundCounter bind(nostd::KeyValueIterable labels); - /* + + /* * Add adds the value to the counter's sum by sending to aggregator. The labels should contain - * the keys and values to be associated with this value. Counters only * accept positive valued updates. + * the keys and values to be associated with this value. Counters only + * accept positive valued updates. * * @param value the numerical representation of the metric being captured * @param labels the set of labels, as key-value pairs @@ -482,9 +491,10 @@ public: template class ValueObserver: public AsynchronousInstrument{ public: - /* + /* * Add adds the value to the counter's sum. The labels should contain - * the keys and values to be associated with this value. Counters only * accept positive valued updates. + * the keys and values to be associated with this value. Counters only + * accept positive valued updates. * * @param value the numerical representation of the metric being captured * @param labels the set of labels, as key-value pairs @@ -495,7 +505,7 @@ public: -``` +```cc // The above Counter and BoundCounter are examples of 1 metric instrument. // The remaining 5 will also be implemented in a similar fashion. class UpDownCounter: public SynchronousInstrument; diff --git a/docs/cpp-metrics-sdk-design.md b/docs/cpp-metrics-sdk-design.md index 5ad4ad80aa..a19aa4b540 100644 --- a/docs/cpp-metrics-sdk-design.md +++ b/docs/cpp-metrics-sdk-design.md @@ -44,7 +44,7 @@ A `MeterProvider` interface must support a `global.SetMeterProvider(MeterProvid The Provider class offers static functions to both get and set the global MeterProvider. Once a user sets the MeterProvider, it will replace the default No-op implementation stored as a private variable and persist for the remainder of the program’s execution. This pattern imitates the TracerProvider used in the Tracing portion of this SDK. -``` +```cc # meter_provider.cc class MeterProvider { @@ -99,9 +99,9 @@ Each distinctly named Meter (i.e. Meters derived from different instrumentation ### Implementation -``` +```cc # meter.h / meter.cc -Class Meter : public API::Meter { +class Meter : public API::Meter { public: /* * Constructor for Meter class @@ -206,7 +206,7 @@ private: * of labels with a single API call. Implementations should find bound metric * instruments that match the key-value pairs in the labels. * - * Arugments: + * Arguments: * labels, labels associated with all measurements in the batch. * records, a KeyValueIterable containing metric instrument names such as * "IntCounter" or "DoubleSumObserver" and the corresponding value @@ -239,7 +239,7 @@ private: -``` +```cc # record.h /* * This class is used to pass checkpointed values from the Meter @@ -372,7 +372,7 @@ The SDK must include the Counter aggregator which maintains a sum and the gauge All operations should be atomic in languages that support them. -``` +```cc # aggregator.cc class Aggregator { public: @@ -381,7 +381,7 @@ public: self.checkpoint_ = nullptr } - /* + /* * Update * * Updates the current value with the new value. @@ -480,7 +480,7 @@ Note: Josh MacDonald is working on implementing a [‘basic’ Processor](https: Design choice: We recommend that we implement the ‘simple’ Processor first as apart of the MVP and then will also implement the ‘basic’ Processor later on. Josh recommended having both for doing different processes. -``` +```cc #processor.cc class Processor { public: @@ -555,7 +555,7 @@ There are two different controllers: Push and Pull. The “Push” Controller wi We recommend implementing the PushController as the initial implementation of the Controller. This Controller is the base controller in the specification. We may also implement the PullController if we have the time to do it. -``` +```cc #push_controller.cc class PushController { @@ -577,6 +577,7 @@ class PushController { MeterProvider Provider { return this.provider_; } + /* * Start (THREAD SAFE) * @@ -635,7 +636,7 @@ There is very little left for the exporter to do other than format the metric up Design choice: Our idea is to take the simple trace example [StdoutExporter](https://github.com/open-telemetry/opentelemetry-cpp/blob/master/examples/simple/stdout_exporter.h) and add Metric functionality to it. This will allow us to verify that what we are implementing in the API and SDK works as intended. The exporter will go through the different metric instruments and print the value stored in their aggregators to stdout, **for simplicity only Sum is shown here, but all aggregators will be implemented**. -``` +```cc # stdout_exporter.cc class StdoutExporter: public exporter { /* @@ -662,7 +663,7 @@ class StdoutExporter: public exporter { -``` +```cc enum class ExportResult { kSuccess, kFailure, diff --git a/docs/cpp-ostream-exporter-design.md b/docs/cpp-ostream-exporter-design.md index 0e7c8726e3..e84da9f800 100644 --- a/docs/cpp-ostream-exporter-design.md +++ b/docs/cpp-ostream-exporter-design.md @@ -58,7 +58,7 @@ Shuts down the exporter. Called when SDK is shut down. This is an opportunity fo In the OStreamExporter there is no cleanup to be done, so there is no need to use the timeout within the `Shutdown` function as it will never be blocking. -``` +```cc class StreamSpanExporter final : public sdktrace::SpanExporter { @@ -142,7 +142,7 @@ The MetricsExporter is called through the Controller in the SDK data path. The e Shutdown() is currently not required for the OStreamMetricsExporter. -``` +```cc class StreamMetricsExporter final : public sdkmeter::MetricsExporter { From 4aa124f58dd80a282da34756059416d34086ae9d Mon Sep 17 00:00:00 2001 From: Bogdan Drutu Date: Fri, 18 Dec 2020 13:16:08 -0800 Subject: [PATCH 2/9] Use opentelemetry-proto from released versions (#457) --- bazel/repository.bzl | 8 ++++++-- third_party/opentelemetry-proto | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/bazel/repository.bzl b/bazel/repository.bzl index 0d26e280b4..b4c618b830 100644 --- a/bazel/repository.bzl +++ b/bazel/repository.bzl @@ -45,10 +45,14 @@ def opentelemetry_cpp_deps(): # OTLP Protocol definition maybe( - native.new_local_repository, + http_archive, name = "com_github_opentelemetry_proto", build_file = "//bazel:opentelemetry_proto.BUILD", - path = "third_party/opentelemetry-proto", + sha256 = "08f090570e0a112bfae276ba37e9c45bf724b64d902a7a001db33123b840ebd6", + strip_prefix = "opentelemetry-proto-0.6.0", + urls = [ + "https://github.com/open-telemetry/opentelemetry-proto/archive/v0.6.0.tar.gz", + ], ) # JSON library diff --git a/third_party/opentelemetry-proto b/third_party/opentelemetry-proto index f11e0538fd..59c488bfb8 160000 --- a/third_party/opentelemetry-proto +++ b/third_party/opentelemetry-proto @@ -1 +1 @@ -Subproject commit f11e0538fd7dc30127ca6bfb2062e5d9f782b77b +Subproject commit 59c488bfb8fb6d0458ad6425758b70259ff4a2bd From c8054c5c46bf81fa57d4d89497fb02b7a863593d Mon Sep 17 00:00:00 2001 From: Tom Tan Date: Fri, 18 Dec 2020 13:44:16 -0800 Subject: [PATCH 3/9] Remove unnecessary call on Span::End() (#449) --- examples/otlp/foo_library/foo_library.cc | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/examples/otlp/foo_library/foo_library.cc b/examples/otlp/foo_library/foo_library.cc index fdbf5b892b..764e6653d4 100644 --- a/examples/otlp/foo_library/foo_library.cc +++ b/examples/otlp/foo_library/foo_library.cc @@ -11,29 +11,23 @@ nostd::shared_ptr get_tracer() return provider->GetTracer("foo_library"); } -// TODO: Remove all calls to span->End() once context memory issue is fixed -// (https://github.com/open-telemetry/opentelemetry-cpp/issues/287) - void f1() { - auto span = get_tracer()->StartSpan("f1"); - span->End(); + auto scoped_span = trace::Scope(get_tracer()->StartSpan("f1")); } void f2() { - auto span = get_tracer()->StartSpan("f2"); + auto scoped_span = trace::Scope(get_tracer()->StartSpan("f2")); f1(); f1(); - span->End(); } } // namespace void foo_library() { - auto span = get_tracer()->StartSpan("library"); + auto scoped_span = trace::Scope(get_tracer()->StartSpan("library")); f2(); - span->End(); } From d787758e335937ca7b7422d55a3ff70c83017d3a Mon Sep 17 00:00:00 2001 From: Mark Date: Sun, 20 Dec 2020 05:45:53 -0700 Subject: [PATCH 4/9] Supporting Logging API .Log() Overloads and GetName() method (#422) --- api/include/opentelemetry/logs/logger.h | 457 +++++++++++++++++- api/include/opentelemetry/logs/noop.h | 2 + api/test/logs/BUILD | 4 +- api/test/logs/CMakeLists.txt | 10 +- api/test/logs/logger_test.cc | 53 +- ...gger_provider_test.cc => provider_test.cc} | 0 exporters/ostream/test/ostream_log_test.cc | 2 +- sdk/include/opentelemetry/sdk/logs/logger.h | 6 + sdk/src/logs/logger.cc | 5 + sdk/test/logs/logger_sdk_test.cc | 13 +- 10 files changed, 508 insertions(+), 44 deletions(-) rename api/test/logs/{logger_provider_test.cc => provider_test.cc} (100%) diff --git a/api/include/opentelemetry/logs/logger.h b/api/include/opentelemetry/logs/logger.h index fc486ec464..c7e4896165 100644 --- a/api/include/opentelemetry/logs/logger.h +++ b/api/include/opentelemetry/logs/logger.h @@ -44,6 +44,9 @@ class Logger public: virtual ~Logger() = default; + /* Returns the name of the logger */ + virtual const nostd::string_view GetName() noexcept = 0; + /** * Each of the following overloaded Log(...) methods * creates a log message with the specific parameters passed. @@ -123,25 +126,457 @@ class Logger /** Wrapper methods that the user could call for convenience when logging **/ - // Set default values for unspecified fields, then call the base Log() method - void Log(Severity severity, nostd::string_view message, core::SystemTimestamp timestamp) noexcept + /** + * Writes a log. + * @param severity The severity of the log + * @param message The message to log + */ + void Log(Severity severity, nostd::string_view message) noexcept { - this->Log(severity, "", message, {}, {}, {}, {}, {}, timestamp); + this->Log(severity, "", message, {}, {}, {}, {}, {}, std::chrono::system_clock::now()); } - // Set default time, and call base Log(severity, message, time) method - void Log(Severity severity, nostd::string_view message) noexcept + /** + * Writes a log. + * @param severity The severity of the log + * @param name The name of the log + * @param message The message to log + */ + void Log(Severity severity, nostd::string_view name, nostd::string_view message) noexcept + { + this->Log(severity, name, message, {}, {}, {}, {}, {}, std::chrono::system_clock::now()); + } + + /** + * Writes a log. + * @param severity The severity of the log + * @param attributes The attributes of the log as a key/value object + */ + template ::value> * = nullptr> + inline void Log(Severity severity, const T &attributes) noexcept + { + this->Log(severity, "", "", std::map{}, attributes, {}, {}, {}, + std::chrono::system_clock::now()); + } + + /** + * Writes a log. + * @param severity The severity of the log + * @param name The name of the log + * @param attributes The attributes of the log as a key/value object + */ + template ::value> * = nullptr> + inline void Log(Severity severity, nostd::string_view name, const T &attributes) noexcept + { + this->Log(severity, name, "", std::map{}, attributes, {}, {}, {}, + std::chrono::system_clock::now()); + } + + /** + * Writes a log. + * @param severity The severity of the log + * @param attributes The attributes of the log as an initializer list + */ + void Log(Severity severity, + std::initializer_list> + attributes) noexcept + { + this->Log(severity, "", "", {}, attributes, {}, {}, {}, std::chrono::system_clock::now()); + } + + /** + * Writes a log. + * @param severity The severity of the log + * @param name The name of the log + * @param attributes The attributes of the log as an initializer list + */ + void Log(Severity severity, + nostd::string_view name, + std::initializer_list> + attributes) noexcept + { + this->Log(severity, name, "", {}, attributes, {}, {}, {}, std::chrono::system_clock::now()); + } + + /** Trace severity overloads **/ + + /** + * Writes a log with a severity of trace. + * @param message The message to log + */ + void Trace(nostd::string_view message) noexcept { this->Log(Severity::kTrace, message); } + + /** + * Writes a log with a severity of trace. + * @param name The name of the log + * @param message The message to log + */ + void Trace(nostd::string_view name, nostd::string_view message) noexcept + { + this->Log(Severity::kTrace, name, message); + } + + /** + * Writes a log with a severity of trace. + * @param attributes The attributes of the log as a key/value object + */ + template ::value> * = nullptr> + void Trace(const T &attributes) noexcept + { + this->Log(Severity::kTrace, attributes); + } + + /** + * Writes a log with a severity of trace. + * @param name The name of the log + * @param attributes The attributes of the log as a key/value object + */ + template ::value> * = nullptr> + void Trace(nostd::string_view name, const T &attributes) noexcept + { + this->Log(Severity::kTrace, name, attributes); + } + + /** + * Writes a log with a severity of trace. + * @param attributes The attributes of the log as an initializer list + */ + void Trace(std::initializer_list> + attributes) noexcept + { + this->Log(Severity::kTrace, attributes); + } + + /** + * Writes a log with a severity of trace. + * @param name The name of the log + * @param attributes The attributes of the log as an initializer list + */ + void Trace(nostd::string_view name, + std::initializer_list> + attributes) noexcept + { + this->Log(Severity::kTrace, name, attributes); + } + + /** Debug severity overloads **/ + + /** + * Writes a log with a severity of debug. + * @param message The message to log + */ + void Debug(nostd::string_view message) noexcept { this->Log(Severity::kDebug, message); } + + /** + * Writes a log with a severity of debug. + * @param name The name of the log + * @param message The message to log + */ + void Debug(nostd::string_view name, nostd::string_view message) noexcept + { + this->Log(Severity::kDebug, name, message); + } + + /** + * Writes a log with a severity of debug. + * @param attributes The attributes of the log as a key/value object + */ + template ::value> * = nullptr> + void Debug(const T &attributes) noexcept + { + this->Log(Severity::kDebug, attributes); + } + + /** + * Writes a log with a severity of debug. + * @param name The name of the log + * @param attributes The attributes of the log as a key/value object + */ + template ::value> * = nullptr> + void Debug(nostd::string_view name, const T &attributes) noexcept + { + this->Log(Severity::kDebug, name, attributes); + } + + /** + * Writes a log with a severity of debug. + * @param attributes The attributes of the log as an initializer list + */ + void Debug(std::initializer_list> + attributes) noexcept { - this->Log(severity, message, std::chrono::system_clock::now()); + this->Log(Severity::kDebug, attributes); } - // Set default severity then call Log(Severity, String message) method - void Log(nostd::string_view message) noexcept { this->Log(Severity::kInfo, message); } + /** + * Writes a log with a severity of debug. + * @param name The name of the log + * @param attributes The attributes of the log as an initializer list + */ + void Debug(nostd::string_view name, + std::initializer_list> + attributes) noexcept + { + this->Log(Severity::kDebug, name, attributes); + } - // TODO: Add more overloaded Log(...) methods with different combiantions of parameters. + /** Info severity overloads **/ - // TODO: Add function aliases such as void debug(), void trace(), void info(), etc. for each - // severity level + /** + * Writes a log with a severity of info. + * @param message The message to log + */ + void Info(nostd::string_view message) noexcept { this->Log(Severity::kInfo, message); } + + /** + * Writes a log with a severity of info. + * @param name The name of the log + * @param message The message to log + */ + void Info(nostd::string_view name, nostd::string_view message) noexcept + { + this->Log(Severity::kInfo, name, message); + } + + /** + * Writes a log with a severity of info. + * @param attributes The attributes of the log as a key/value object + */ + template ::value> * = nullptr> + void Info(const T &attributes) noexcept + { + this->Log(Severity::kInfo, attributes); + } + + /** + * Writes a log with a severity of info. + * @param name The name of the log + * @param attributes The attributes of the log as a key/value object + */ + template ::value> * = nullptr> + void Info(nostd::string_view name, const T &attributes) noexcept + { + this->Log(Severity::kInfo, name, attributes); + } + + /** + * Writes a log with a severity of info. + * @param attributes The attributes of the log as an initializer list + */ + void Info(std::initializer_list> + attributes) noexcept + { + this->Log(Severity::kInfo, attributes); + } + + /** + * Writes a log with a severity of info. + * @param name The name of the log + * @param attributes The attributes of the log as an initializer list + */ + void Info(nostd::string_view name, + std::initializer_list> + attributes) noexcept + { + this->Log(Severity::kInfo, name, attributes); + } + + /** Warn severity overloads **/ + + /** + * Writes a log with a severity of warn. + * @param message The message to log + */ + void Warn(nostd::string_view message) noexcept { this->Log(Severity::kWarn, message); } + + /** + * Writes a log with a severity of warn. + * @param name The name of the log + * @param message The message to log + */ + void Warn(nostd::string_view name, nostd::string_view message) noexcept + { + this->Log(Severity::kWarn, name, message); + } + + /** + * Writes a log with a severity of warn. + * @param attributes The attributes of the log as a key/value object + */ + template ::value> * = nullptr> + void Warn(const T &attributes) noexcept + { + this->Log(Severity::kWarn, attributes); + } + + /** + * Writes a log with a severity of warn. + * @param name The name of the log + * @param attributes The attributes of the log as a key/value object + */ + template ::value> * = nullptr> + void Warn(nostd::string_view name, const T &attributes) noexcept + { + this->Log(Severity::kWarn, name, attributes); + } + + /** + * Writes a log with a severity of warn. + * @param attributes The attributes of the log as an initializer list + */ + void Warn(std::initializer_list> + attributes) noexcept + { + this->Log(Severity::kWarn, attributes); + } + + /** + * Writes a log with a severity of warn. + * @param name The name of the log + * @param attributes The attributes of the log as an initializer list + */ + void Warn(nostd::string_view name, + std::initializer_list> + attributes) noexcept + { + this->Log(Severity::kWarn, name, attributes); + } + + /** Error severity overloads **/ + + /** + * Writes a log with a severity of error. + * @param message The message to log + */ + void Error(nostd::string_view message) noexcept { this->Log(Severity::kError, message); } + + /** + * Writes a log with a severity of error. + * @param name The name of the log + * @param message The message to log + */ + void Error(nostd::string_view name, nostd::string_view message) noexcept + { + this->Log(Severity::kError, name, message); + } + + /** + * Writes a log with a severity of error. + * @param attributes The attributes of the log as a key/value object + */ + template ::value> * = nullptr> + void Error(const T &attributes) noexcept + { + this->Log(Severity::kError, attributes); + } + + /** + * Writes a log with a severity of error. + * @param name The name of the log + * @param attributes The attributes of the log as a key/value object + */ + template ::value> * = nullptr> + void Error(nostd::string_view name, const T &attributes) noexcept + { + this->Log(Severity::kError, name, attributes); + } + + /** + * Writes a log with a severity of error. + * @param attributes The attributes of the log as an initializer list + */ + void Error(std::initializer_list> + attributes) noexcept + { + this->Log(Severity::kError, attributes); + } + + /** + * Writes a log with a severity of error. + * @param name The name of the log + * @param attributes The attributes of the log as an initializer list + */ + void Error(nostd::string_view name, + std::initializer_list> + attributes) noexcept + { + this->Log(Severity::kError, name, attributes); + } + + /** Fatal severity overloads **/ + + /** + * Writes a log with a severity of fatal. + * @param message The message to log + */ + void Fatal(nostd::string_view message) noexcept { this->Log(Severity::kFatal, message); } + + /** + * Writes a log with a severity of fatal. + * @param name The name of the log + * @param message The message to log + */ + void Fatal(nostd::string_view name, nostd::string_view message) noexcept + { + this->Log(Severity::kFatal, name, message); + } + + /** + * Writes a log with a severity of fatal. + * @param attributes The attributes of the log as a key/value object + */ + template ::value> * = nullptr> + void Fatal(const T &attributes) noexcept + { + this->Log(Severity::kFatal, attributes); + } + + /** + * Writes a log with a severity of fatal. + * @param name The name of the log + * @param attributes The attributes of the log as a key/value object + */ + template ::value> * = nullptr> + void Fatal(nostd::string_view name, const T &attributes) noexcept + { + this->Log(Severity::kFatal, name, attributes); + } + + /** + * Writes a log with a severity of fatal. + * @param attributes The attributes of the log as an initializer list + */ + void Fatal(std::initializer_list> + attributes) noexcept + { + this->Log(Severity::kFatal, attributes); + } + + /** + * Writes a log with a severity of fatal. + * @param name The name of the log + * @param attributes The attributes of the log as an initializer list + */ + void Fatal(nostd::string_view name, + std::initializer_list> + attributes) noexcept + { + this->Log(Severity::kFatal, name, attributes); + } }; } // namespace logs OPENTELEMETRY_END_NAMESPACE diff --git a/api/include/opentelemetry/logs/noop.h b/api/include/opentelemetry/logs/noop.h index 8a19e1a677..86738692c1 100644 --- a/api/include/opentelemetry/logs/noop.h +++ b/api/include/opentelemetry/logs/noop.h @@ -50,6 +50,8 @@ namespace logs class NoopLogger final : public Logger { public: + const nostd::string_view GetName() noexcept override { return "noop logger"; } + void Log(Severity severity, nostd::string_view name, nostd::string_view body, diff --git a/api/test/logs/BUILD b/api/test/logs/BUILD index 899400b2e8..cba6f19220 100644 --- a/api/test/logs/BUILD +++ b/api/test/logs/BUILD @@ -1,9 +1,9 @@ load("//bazel:otel_cc_benchmark.bzl", "otel_cc_benchmark") cc_test( - name = "logger_provider_test", + name = "provider_test", srcs = [ - "logger_provider_test.cc", + "provider_test.cc", ], deps = [ "//api", diff --git a/api/test/logs/CMakeLists.txt b/api/test/logs/CMakeLists.txt index 9b1b3b0e7d..a5f9c04084 100644 --- a/api/test/logs/CMakeLists.txt +++ b/api/test/logs/CMakeLists.txt @@ -1,9 +1,9 @@ -foreach(testname logger_provider_test logger_test) - add_executable(${testname} "${testname}.cc") - target_link_libraries(${testname} ${GTEST_BOTH_LIBRARIES} +foreach(testname provider_test logger_test) + add_executable(logs_api_${testname} "${testname}.cc") + target_link_libraries(logs_api_${testname} ${GTEST_BOTH_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} opentelemetry_api) gtest_add_tests( - TARGET ${testname} + TARGET logs_api_${testname} TEST_PREFIX logs. - TEST_LIST ${testname}) + TEST_LIST logs_api_${testname}) endforeach() diff --git a/api/test/logs/logger_test.cc b/api/test/logs/logger_test.cc index d875a54c35..de03c8d403 100644 --- a/api/test/logs/logger_test.cc +++ b/api/test/logs/logger_test.cc @@ -14,40 +14,61 @@ using opentelemetry::nostd::shared_ptr; using opentelemetry::nostd::span; using opentelemetry::nostd::string_view; +// Check that the default logger is a noop logger instance TEST(Logger, GetLoggerDefault) { auto lp = Provider::GetLoggerProvider(); auto logger = lp->GetLogger("TestLogger"); + auto name = logger->GetName(); EXPECT_NE(nullptr, logger); + EXPECT_EQ(name, "noop logger"); } -TEST(Logger, GetNoopLoggerName) -{ - auto lp = Provider::GetLoggerProvider(); - auto logger = lp->GetLogger("TestLogger"); -} - +// Test the two additional overloads for GetLogger() TEST(Logger, GetNoopLoggerNameWithArgs) { auto lp = Provider::GetLoggerProvider(); + // GetLogger(name, list(args)) std::array sv{"string"}; span args{sv}; - auto logger = lp->GetLogger("NoopLoggerWithArgs", args); - // should probably also test that arguments were set properly too - // by adding a getArgs() method in NoopLogger + lp->GetLogger("NoopLoggerWithArgs", args); + + // GetLogger(name, string options) + lp->GetLogger("NoopLoggerWithOptions", "options"); } -TEST(Logger, NoopLog) +// Test the Log() overloads +TEST(Logger, LogMethodOverloads) { auto lp = Provider::GetLoggerProvider(); auto logger = lp->GetLogger("TestLogger"); - logger->Log("Noop log name"); + + // Create a map to test the logs with + std::map m = {{"key1", "value1"}}; + + // Log overloads + logger->Log(Severity::kTrace, "Test log message"); + logger->Log(Severity::kInfo, "Logging a message", "Test log message"); + logger->Log(Severity::kDebug, m); + logger->Log(Severity::kWarn, "Logging a map", m); + logger->Log(Severity::kError, {{"key1", "value 1"}, {"key2", 2}}); + logger->Log(Severity::kFatal, "Logging an initializer list", {{"key1", "value 1"}, {"key2", 2}}); + + // Severity methods + logger->Trace("Test log message"); + logger->Debug("Logging a message", "Test log message"); + logger->Info(m); + logger->Warn("Logging a map", m); + logger->Error({{"key1", "value 1"}, {"key2", 2}}); + logger->Fatal("Logging an initializer list", {{"key1", "value 1"}, {"key2", 2}}); } // Define a basic Logger class class TestLogger : public Logger { + const opentelemetry::nostd::string_view GetName() noexcept override { return "test logger"; } + void Log(Severity severity, string_view name, string_view body, @@ -82,11 +103,7 @@ TEST(Logger, PushLoggerImplementation) auto lp = Provider::GetLoggerProvider(); - // GetLogger(name, options) function + // Check that the implementation was pushed by calling TestLogger's GetName() auto logger = lp->GetLogger("TestLogger"); - - // GetLogger(name, args) function - std::array sv{"string"}; - span args{sv}; - auto logger2 = lp->GetLogger("TestLogger2", args); -} + ASSERT_EQ("test logger", logger->GetName()); +} \ No newline at end of file diff --git a/api/test/logs/logger_provider_test.cc b/api/test/logs/provider_test.cc similarity index 100% rename from api/test/logs/logger_provider_test.cc rename to api/test/logs/provider_test.cc diff --git a/exporters/ostream/test/ostream_log_test.cc b/exporters/ostream/test/ostream_log_test.cc index e91528b20a..b091a71b3d 100644 --- a/exporters/ostream/test/ostream_log_test.cc +++ b/exporters/ostream/test/ostream_log_test.cc @@ -253,7 +253,7 @@ TEST(OStreamLogExporter, IntegrationTest) // Write a log to ostream exporter opentelemetry::core::SystemTimestamp now(std::chrono::system_clock::now()); - logger->Log(opentelemetry::logs::Severity::kDebug, "Hello", now); + logger->Log(opentelemetry::logs::Severity::kDebug, "", "Hello", {}, {}, {}, {}, {}, now); // Restore cout's original streambuf std::cout.rdbuf(original); diff --git a/sdk/include/opentelemetry/sdk/logs/logger.h b/sdk/include/opentelemetry/sdk/logs/logger.h index 385ba3586b..8cf21a09f6 100644 --- a/sdk/include/opentelemetry/sdk/logs/logger.h +++ b/sdk/include/opentelemetry/sdk/logs/logger.h @@ -34,11 +34,17 @@ class Logger final : public opentelemetry::logs::Logger public: /** * Initialize a new logger. + * @param name The name of this logger instance * @param logger_provider The logger provider that owns this logger. */ explicit Logger(opentelemetry::nostd::string_view name, std::shared_ptr logger_provider) noexcept; + /** + * Returns the name of this logger. + */ + const opentelemetry::nostd::string_view GetName() noexcept override; + /** * Writes a log record into the processor. * @param severity the severity level of the log event. diff --git a/sdk/src/logs/logger.cc b/sdk/src/logs/logger.cc index 6e201da04e..ad87ca505a 100644 --- a/sdk/src/logs/logger.cc +++ b/sdk/src/logs/logger.cc @@ -27,6 +27,11 @@ Logger::Logger(opentelemetry::nostd::string_view name, : logger_name_(std::string(name)), logger_provider_(logger_provider) {} +const opentelemetry::nostd::string_view Logger::GetName() noexcept +{ + return logger_name_; +} + /** * Create and populate recordable with the log event's fields passed in. * The timestamp, severity, traceid, spanid, and traceflags, are injected diff --git a/sdk/test/logs/logger_sdk_test.cc b/sdk/test/logs/logger_sdk_test.cc index e1993f15ec..669ae2fbc0 100644 --- a/sdk/test/logs/logger_sdk_test.cc +++ b/sdk/test/logs/logger_sdk_test.cc @@ -31,7 +31,7 @@ TEST(LoggerSDK, LogToNullProcessor) auto logger = lp->GetLogger("logger"); // Log a sample log record to a nullptr processor - logger->Log("Test log"); + logger->Debug("Test log"); } class MockProcessor final : public LogProcessor @@ -56,11 +56,11 @@ class MockProcessor final : public LogProcessor // Cast the recordable received into a concrete LogRecord type auto copy = std::shared_ptr(static_cast(record.release())); - // Copy over the received log record's name, body, timestamp fields over to the recordable + // Copy over the received log record's severity, name, and body fields over to the recordable // passed in the constructor record_received_->SetSeverity(copy->GetSeverity()); + record_received_->SetName(copy->GetName()); record_received_->SetBody(copy->GetBody()); - record_received_->SetTimestamp(copy->GetTimestamp()); } bool ForceFlush(std::chrono::microseconds timeout = std::chrono::microseconds(0)) noexcept { @@ -92,10 +92,9 @@ TEST(LoggerSDK, LogToAProcessor) ASSERT_EQ(processor, lp->GetProcessor()); // Check that the recordable created by the Log() statement is set properly - opentelemetry::core::SystemTimestamp now(std::chrono::system_clock::now()); - logger->Log(opentelemetry::logs::Severity::kWarn, "Message", now); + logger->Log(opentelemetry::logs::Severity::kWarn, "Log Name", "Log Message"); ASSERT_EQ(shared_recordable->GetSeverity(), opentelemetry::logs::Severity::kWarn); - ASSERT_EQ(shared_recordable->GetBody(), "Message"); - ASSERT_EQ(shared_recordable->GetTimestamp().time_since_epoch(), now.time_since_epoch()); + ASSERT_EQ(shared_recordable->GetName(), "Log Name"); + ASSERT_EQ(shared_recordable->GetBody(), "Log Message"); } From b80dccbfffb65b97746aa45f491dcd0c1583ecec Mon Sep 17 00:00:00 2001 From: Lalit Kumar Bhasin Date: Tue, 22 Dec 2020 00:50:38 +0530 Subject: [PATCH 5/9] Add supported platforms in README (#452) --- README.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/README.md b/README.md index 8a477a7ef7..7c352aeecd 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,19 @@ Please note that supporting the [C Programming Language](https://en.wikipedia.org/wiki/C_(programming_language)) is not a goal of the current project. +## Supported Development Platforms + + Our CI pipeline builds and tests on following `x86-64` platforms: + +* ubuntu-18.04 ( Default GCC Compiler - 7.5.0 ) +* ubuntu-18.04 ( GCC 4.8 with -std=c++11 flag) +* ubuntu-20.04 ( Default GCC Compiler - 9.3.0 with -std=c++20 flags ) +* macOS 10.15 ( Xcode 12.2 ) +* Windows Server 2019 (Visual Studio Enterprise 2019 ) + +In general, the code shipped from this repository should build on all platforms having C++ compiler with [supported C++ standards](#supported-c-versions) + + ## Installation Please refer to [INSTALL.md](./INSTALL.md). From 5e946f96c313c4ee36b77492d5682fe18708f92a Mon Sep 17 00:00:00 2001 From: Karen Xu Date: Mon, 21 Dec 2020 22:40:04 -0500 Subject: [PATCH 6/9] Add batch log processor implementation with test coverage (#434) --- .../sdk/logs/batch_log_processor.h | 139 +++++++++ sdk/src/logs/CMakeLists.txt | 2 +- sdk/src/logs/batch_log_processor.cc | 213 ++++++++++++++ sdk/test/logs/BUILD | 11 + sdk/test/logs/CMakeLists.txt | 4 +- sdk/test/logs/batch_log_processor_test.cc | 276 ++++++++++++++++++ third_party/opentelemetry-proto | 2 +- 7 files changed, 643 insertions(+), 4 deletions(-) create mode 100644 sdk/include/opentelemetry/sdk/logs/batch_log_processor.h create mode 100644 sdk/src/logs/batch_log_processor.cc create mode 100644 sdk/test/logs/batch_log_processor_test.cc diff --git a/sdk/include/opentelemetry/sdk/logs/batch_log_processor.h b/sdk/include/opentelemetry/sdk/logs/batch_log_processor.h new file mode 100644 index 0000000000..f27bf231fc --- /dev/null +++ b/sdk/include/opentelemetry/sdk/logs/batch_log_processor.h @@ -0,0 +1,139 @@ +/* + * Copyright The OpenTelemetry 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 + * + * http://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. + */ + +#pragma once + +#include "opentelemetry/sdk/common/circular_buffer.h" +#include "opentelemetry/sdk/logs/exporter.h" +#include "opentelemetry/sdk/logs/processor.h" + +#include +#include +#include + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ + +namespace logs +{ + +/** + * This is an implementation of the LogProcessor which creates batches of finished logs and passes + * the export-friendly log data representations to the configured LogExporter. + */ +class BatchLogProcessor : public LogProcessor +{ +public: + /** + * Creates a batch log processor by configuring the specified exporter and other parameters + * as per the official, language-agnostic opentelemetry specs. + * + * @param exporter - The backend exporter to pass the logs to + * @param max_queue_size - The maximum buffer/queue size. After the size is reached, logs are + * dropped. + * @param scheduled_delay_millis - The time interval between two consecutive exports. + * @param max_export_batch_size - The maximum batch size of every export. It must be smaller or + * equal to max_queue_size + */ + explicit BatchLogProcessor( + std::unique_ptr &&exporter, + const size_t max_queue_size = 2048, + const std::chrono::milliseconds scheduled_delay_millis = std::chrono::milliseconds(5000), + const size_t max_export_batch_size = 512); + + /** Makes a new recordable **/ + std::unique_ptr MakeRecordable() noexcept override; + + /** + * Called when the Logger's log method creates a log record + * @param record the log record + */ + + void OnReceive(std::unique_ptr &&record) noexcept override; + + /** + * Export all log records that have not been exported yet. + * + * NOTE: Timeout functionality not supported yet. + */ + bool ForceFlush( + std::chrono::microseconds timeout = std::chrono::microseconds::max()) noexcept override; + + /** + * Shuts down the processor and does any cleanup required. Completely drains the buffer/queue of + * all its logs and passes them to the exporter. Any subsequent calls to + * ForceFlush or Shutdown will return immediately without doing anything. + * + * NOTE: Timeout functionality not supported yet. + */ + bool Shutdown( + std::chrono::microseconds timeout = std::chrono::microseconds::max()) noexcept override; + + /** + * Class destructor which invokes the Shutdown() method. + */ + virtual ~BatchLogProcessor() override; + +private: + /** + * The background routine performed by the worker thread. + */ + void DoBackgroundWork(); + + /** + * Exports all logs to the configured exporter. + * + * @param was_force_flush_called - A flag to check if the current export is the result + * of a call to ForceFlush method. If true, then we have to + * notify the main thread to wake it up in the ForceFlush + * method. + */ + void Export(const bool was_for_flush_called); + + /** + * Called when Shutdown() is invoked. Completely drains the queue of all log records and + * passes them to the exporter. + */ + void DrainQueue(); + + /* The configured backend log exporter */ + std::unique_ptr exporter_; + + /* Configurable parameters as per the official *trace* specs */ + const size_t max_queue_size_; + const std::chrono::milliseconds scheduled_delay_millis_; + const size_t max_export_batch_size_; + + /* Synchronization primitives */ + std::condition_variable cv_, force_flush_cv_; + std::mutex cv_m_, force_flush_cv_m_; + + /* The buffer/queue to which the ended logs are added */ + common::CircularBuffer buffer_; + + /* Important boolean flags to handle the workflow of the processor */ + std::atomic is_shutdown_{false}; + std::atomic is_force_flush_{false}; + std::atomic is_force_flush_notified_{false}; + + /* The background worker thread */ + std::thread worker_thread_; +}; + +} // namespace logs +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE diff --git a/sdk/src/logs/CMakeLists.txt b/sdk/src/logs/CMakeLists.txt index e2e7c2c915..44d8909472 100644 --- a/sdk/src/logs/CMakeLists.txt +++ b/sdk/src/logs/CMakeLists.txt @@ -1,4 +1,4 @@ add_library(opentelemetry_logs logger_provider.cc logger.cc - simple_log_processor.cc) + simple_log_processor.cc batch_log_processor.cc) target_link_libraries(opentelemetry_logs opentelemetry_common) diff --git a/sdk/src/logs/batch_log_processor.cc b/sdk/src/logs/batch_log_processor.cc new file mode 100644 index 0000000000..e61f9a31fd --- /dev/null +++ b/sdk/src/logs/batch_log_processor.cc @@ -0,0 +1,213 @@ +/* + * Copyright The OpenTelemetry 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 + * + * http://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. + */ + +#include "opentelemetry/sdk/logs/batch_log_processor.h" + +#include +using opentelemetry::sdk::common::AtomicUniquePtr; +using opentelemetry::sdk::common::CircularBufferRange; + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace logs +{ +BatchLogProcessor::BatchLogProcessor(std::unique_ptr &&exporter, + const size_t max_queue_size, + const std::chrono::milliseconds scheduled_delay_millis, + const size_t max_export_batch_size) + : exporter_(std::move(exporter)), + max_queue_size_(max_queue_size), + scheduled_delay_millis_(scheduled_delay_millis), + max_export_batch_size_(max_export_batch_size), + buffer_(max_queue_size_), + worker_thread_(&BatchLogProcessor::DoBackgroundWork, this) +{} + +std::unique_ptr BatchLogProcessor::MakeRecordable() noexcept +{ + return exporter_->MakeRecordable(); +} + +void BatchLogProcessor::OnReceive(std::unique_ptr &&record) noexcept +{ + if (is_shutdown_.load() == true) + { + return; + } + + if (buffer_.Add(record) == false) + { + return; + } + + // If the queue gets at least half full a preemptive notification is + // sent to the worker thread to start a new export cycle. + if (buffer_.size() >= max_queue_size_ / 2) + { + // signal the worker thread + cv_.notify_one(); + } +} + +bool BatchLogProcessor::ForceFlush(std::chrono::microseconds timeout) noexcept +{ + if (is_shutdown_.load() == true) + { + return false; + } + + is_force_flush_ = true; + + // Keep attempting to wake up the worker thread + while (is_force_flush_.load() == true) + { + cv_.notify_one(); + } + + // Now wait for the worker thread to signal back from the Export method + std::unique_lock lk(force_flush_cv_m_); + while (is_force_flush_notified_.load() == false) + { + force_flush_cv_.wait(lk); + } + + // Notify the worker thread + is_force_flush_notified_ = false; + + return true; +} + +void BatchLogProcessor::DoBackgroundWork() +{ + auto timeout = scheduled_delay_millis_; + + while (true) + { + // Wait for `timeout` milliseconds + std::unique_lock lk(cv_m_); + cv_.wait_for(lk, timeout); + + if (is_shutdown_.load() == true) + { + DrainQueue(); + return; + } + + bool was_force_flush_called = is_force_flush_.load(); + + // Check if this export was the result of a force flush. + if (was_force_flush_called == true) + { + // Since this export was the result of a force flush, signal the + // main thread that the worker thread has been notified + is_force_flush_ = false; + } + else + { + // If the buffer was empty during the entire `timeout` time interval, + // go back to waiting. If this was a spurious wake-up, we export only if + // `buffer_` is not empty. This is acceptable because batching is a best + // mechanism effort here. + if (buffer_.empty() == true) + { + continue; + } + } + + auto start = std::chrono::steady_clock::now(); + Export(was_force_flush_called); + auto end = std::chrono::steady_clock::now(); + auto duration = std::chrono::duration_cast(end - start); + + // Subtract the duration of this export call from the next `timeout`. + timeout = scheduled_delay_millis_ - duration; + } +} + +void BatchLogProcessor::Export(const bool was_force_flush_called) +{ + std::vector> records_arr; + + size_t num_records_to_export; + + if (was_force_flush_called == true) + { + num_records_to_export = buffer_.size(); + } + else + { + num_records_to_export = + buffer_.size() >= max_export_batch_size_ ? max_export_batch_size_ : buffer_.size(); + } + + buffer_.Consume( + num_records_to_export, [&](CircularBufferRange> range) noexcept { + range.ForEach([&](AtomicUniquePtr &ptr) { + std::unique_ptr swap_ptr = std::unique_ptr(nullptr); + ptr.Swap(swap_ptr); + records_arr.push_back(std::unique_ptr(swap_ptr.release())); + return true; + }); + }); + + exporter_->Export( + nostd::span>(records_arr.data(), records_arr.size())); + + // Notify the main thread in case this export was the result of a force flush. + if (was_force_flush_called == true) + { + is_force_flush_notified_ = true; + while (is_force_flush_notified_.load() == true) + { + force_flush_cv_.notify_one(); + } + } +} + +void BatchLogProcessor::DrainQueue() +{ + while (buffer_.empty() == false) + { + Export(false); + } +} + +bool BatchLogProcessor::Shutdown(std::chrono::microseconds timeout) noexcept +{ + is_shutdown_.store(true); + + cv_.notify_one(); + worker_thread_.join(); + if (exporter_ != nullptr) + { + return exporter_->Shutdown(); + } + + return true; +} + +BatchLogProcessor::~BatchLogProcessor() +{ + if (is_shutdown_.load() == false) + { + Shutdown(); + } +} + +} // namespace logs +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE diff --git a/sdk/test/logs/BUILD b/sdk/test/logs/BUILD index 9e12e08ab9..e70527243f 100644 --- a/sdk/test/logs/BUILD +++ b/sdk/test/logs/BUILD @@ -42,3 +42,14 @@ cc_test( "@com_google_googletest//:gtest_main", ], ) + +cc_test( + name = "batch_log_processor_test", + srcs = [ + "batch_log_processor_test.cc", + ], + deps = [ + "//sdk/src/logs", + "@com_google_googletest//:gtest_main", + ], +) diff --git a/sdk/test/logs/CMakeLists.txt b/sdk/test/logs/CMakeLists.txt index f59c6a1926..84b865d226 100644 --- a/sdk/test/logs/CMakeLists.txt +++ b/sdk/test/logs/CMakeLists.txt @@ -1,5 +1,5 @@ -foreach(testname logger_provider_sdk_test logger_sdk_test - simple_log_processor_test log_record_test) +foreach(testname logger_provider_sdk_test logger_sdk_test log_record_test + simple_log_processor_test batch_log_processor_test) add_executable(${testname} "${testname}.cc") target_link_libraries(${testname} ${GTEST_BOTH_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} opentelemetry_logs) diff --git a/sdk/test/logs/batch_log_processor_test.cc b/sdk/test/logs/batch_log_processor_test.cc new file mode 100644 index 0000000000..baf27de839 --- /dev/null +++ b/sdk/test/logs/batch_log_processor_test.cc @@ -0,0 +1,276 @@ +/* + * Copyright The OpenTelemetry 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 + * + * http://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. + */ + +#include "opentelemetry/sdk/logs/batch_log_processor.h" +#include "opentelemetry/sdk/logs/exporter.h" +#include "opentelemetry/sdk/logs/log_record.h" + +#include +#include +#include + +using namespace opentelemetry::sdk::logs; + +/** + * A sample log exporter + * for testing the batch log processor + */ +class MockLogExporter final : public LogExporter +{ +public: + MockLogExporter(std::shared_ptr>> logs_received, + std::shared_ptr> is_shutdown, + std::shared_ptr> is_export_completed, + const std::chrono::milliseconds export_delay = std::chrono::milliseconds(0)) + : logs_received_(logs_received), + is_shutdown_(is_shutdown), + is_export_completed_(is_export_completed), + export_delay_(export_delay) + {} + + std::unique_ptr MakeRecordable() noexcept + { + return std::unique_ptr(new LogRecord()); + } + + // Export method stores the logs received into a shared list of record names + ExportResult Export( + const opentelemetry::nostd::span> &records) noexcept override + { + *is_export_completed_ = false; // Meant exclusively to test scheduled_delay_millis + + for (auto &record : records) + { + auto log = std::unique_ptr(static_cast(record.release())); + if (log != nullptr) + { + logs_received_->push_back(std::move(log)); + } + } + + *is_export_completed_ = true; + return ExportResult::kSuccess; + } + + // toggles the boolean flag marking this exporter as shut down + bool Shutdown( + std::chrono::microseconds timeout = std::chrono::microseconds::max()) noexcept override + { + *is_shutdown_ = true; + return true; + } + +private: + std::shared_ptr>> logs_received_; + std::shared_ptr> is_shutdown_; + std::shared_ptr> is_export_completed_; + const std::chrono::milliseconds export_delay_; +}; + +/** + * A fixture class for testing the BatchLogProcessor class that uses the TestExporter defined above. + */ +class BatchLogProcessorTest : public testing::Test // ::testing::Test +{ +public: + // returns a batch log processor that received a batch of log records, a shared pointer to a + // is_shutdown flag, and the processor configuration options (default if unspecified) + std::shared_ptr GetMockProcessor( + std::shared_ptr>> logs_received, + std::shared_ptr> is_shutdown, + std::shared_ptr> is_export_completed = + std::shared_ptr>(new std::atomic(false)), + const std::chrono::milliseconds export_delay = std::chrono::milliseconds(0), + const std::chrono::milliseconds scheduled_delay_millis = std::chrono::milliseconds(5000), + const size_t max_queue_size = 2048, + const size_t max_export_batch_size = 512) + { + return std::shared_ptr( + new BatchLogProcessor(std::unique_ptr(new MockLogExporter( + logs_received, is_shutdown, is_export_completed, export_delay)), + max_queue_size, scheduled_delay_millis, max_export_batch_size)); + } +}; + +TEST_F(BatchLogProcessorTest, TestShutdown) +{ + // initialize a batch log processor with the test exporter + std::shared_ptr>> logs_received( + new std::vector>); + std::shared_ptr> is_shutdown(new std::atomic(false)); + + auto batch_processor = GetMockProcessor(logs_received, is_shutdown); + + // Create a few test log records and send them to the processor + const int num_logs = 3; + + for (int i = 0; i < num_logs; ++i) + { + auto log = batch_processor->MakeRecordable(); + log->SetName("Log" + std::to_string(i)); + batch_processor->OnReceive(std::move(log)); + } + + // Test that shutting down the processor will first wait for the + // current batch of logs to be sent to the log exporter + // by checking the number of logs sent and the names of the logs sent + EXPECT_EQ(true, batch_processor->Shutdown()); + + EXPECT_EQ(num_logs, logs_received->size()); + + // Assume logs are received by exporter in same order as sent by processor + for (int i = 0; i < num_logs; ++i) + { + EXPECT_EQ("Log" + std::to_string(i), logs_received->at(i)->GetName()); + } + + // Also check that the processor is shut down at the end + EXPECT_TRUE(is_shutdown->load()); +} + +TEST_F(BatchLogProcessorTest, TestForceFlush) +{ + std::shared_ptr> is_shutdown(new std::atomic(false)); + std::shared_ptr>> logs_received( + new std::vector>); + + auto batch_processor = GetMockProcessor(logs_received, is_shutdown); + const int num_logs = 2048; + + for (int i = 0; i < num_logs; ++i) + { + auto log = batch_processor->MakeRecordable(); + log->SetName("Log" + std::to_string(i)); + batch_processor->OnReceive(std::move(log)); + } + + EXPECT_TRUE(batch_processor->ForceFlush()); + + EXPECT_EQ(num_logs, logs_received->size()); + for (int i = 0; i < num_logs; ++i) + { + EXPECT_EQ("Log" + std::to_string(i), logs_received->at(i)->GetName()); + } + + // Create some more logs to make sure that the processor still works + for (int i = 0; i < num_logs; ++i) + { + auto log = batch_processor->MakeRecordable(); + log->SetName("Log" + std::to_string(i)); + batch_processor->OnReceive(std::move(log)); + } + + EXPECT_TRUE(batch_processor->ForceFlush()); + + EXPECT_EQ(num_logs * 2, logs_received->size()); + for (int i = 0; i < num_logs * 2; ++i) + { + EXPECT_EQ("Log" + std::to_string(i % num_logs), logs_received->at(i)->GetName()); + } +} + +TEST_F(BatchLogProcessorTest, TestManyLogsLoss) +{ + /* Test that when exporting more than max_queue_size logs, some are most likely lost*/ + + std::shared_ptr> is_shutdown(new std::atomic(false)); + std::shared_ptr>> logs_received( + new std::vector>); + + const int max_queue_size = 4096; + + auto batch_processor = GetMockProcessor(logs_received, is_shutdown); + + // Create max_queue_size log records + for (int i = 0; i < max_queue_size; ++i) + { + auto log = batch_processor->MakeRecordable(); + log->SetName("Log" + std::to_string(i)); + batch_processor->OnReceive(std::move(log)); + } + + EXPECT_TRUE(batch_processor->ForceFlush()); + + // Log should be exported by now + EXPECT_GE(max_queue_size, logs_received->size()); +} + +TEST_F(BatchLogProcessorTest, TestManyLogsLossLess) +{ + /* Test that no logs are lost when sending max_queue_size logs */ + + std::shared_ptr> is_shutdown(new std::atomic(false)); + std::shared_ptr>> logs_received( + new std::vector>); + auto batch_processor = GetMockProcessor(logs_received, is_shutdown); + + const int num_logs = 2048; + + for (int i = 0; i < num_logs; ++i) + { + auto log = batch_processor->MakeRecordable(); + log->SetName("Log" + std::to_string(i)); + batch_processor->OnReceive(std::move(log)); + } + + EXPECT_TRUE(batch_processor->ForceFlush()); + + EXPECT_EQ(num_logs, logs_received->size()); + for (int i = 0; i < num_logs; ++i) + { + EXPECT_EQ("Log" + std::to_string(i), logs_received->at(i)->GetName()); + } +} + +TEST_F(BatchLogProcessorTest, TestScheduledDelayMillis) +{ + /* Test that max_export_batch_size logs are exported every scheduled_delay_millis + seconds */ + + std::shared_ptr> is_shutdown(new std::atomic(false)); + std::shared_ptr> is_export_completed(new std::atomic(false)); + std::shared_ptr>> logs_received( + new std::vector>); + + const std::chrono::milliseconds export_delay(0); + const std::chrono::milliseconds scheduled_delay_millis(2000); + const size_t max_export_batch_size = 512; + + auto batch_processor = GetMockProcessor(logs_received, is_shutdown, is_export_completed, + export_delay, scheduled_delay_millis); + + for (int i = 0; i < max_export_batch_size; ++i) + { + auto log = batch_processor->MakeRecordable(); + log->SetName("Log" + std::to_string(i)); + batch_processor->OnReceive(std::move(log)); + } + // Sleep for scheduled_delay_millis milliseconds + std::this_thread::sleep_for(scheduled_delay_millis); + + // small delay to give time to export, which is being performed + // asynchronously by the worker thread (this thread will not + // forcibly join() the main thread unless processor's shutdown() is called). + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + + // Logs should be exported by now + EXPECT_TRUE(is_export_completed->load()); + EXPECT_EQ(max_export_batch_size, logs_received->size()); + for (size_t i = 0; i < max_export_batch_size; ++i) + { + EXPECT_EQ("Log" + std::to_string(i), logs_received->at(i)->GetName()); + } +} diff --git a/third_party/opentelemetry-proto b/third_party/opentelemetry-proto index 59c488bfb8..f11e0538fd 160000 --- a/third_party/opentelemetry-proto +++ b/third_party/opentelemetry-proto @@ -1 +1 @@ -Subproject commit 59c488bfb8fb6d0458ad6425758b70259ff4a2bd +Subproject commit f11e0538fd7dc30127ca6bfb2062e5d9f782b77b From 1e7b9d8a950649809d3957a00e230e43b59a1c86 Mon Sep 17 00:00:00 2001 From: Max Golovanov Date: Mon, 21 Dec 2020 21:57:48 -0800 Subject: [PATCH 7/9] Using STD library for API surface (#374) --- .github/workflows/ci.yml | 19 +- CMakeLists.txt | 42 + CMakeSettings.json | 57 + api/CMakeLists.txt | 6 + .../opentelemetry/common/attribute_value.h | 44 +- .../opentelemetry/common/key_value_iterable.h | 18 + api/include/opentelemetry/logs/logger.h | 1 + .../opentelemetry/nostd/function_ref.h | 14 + .../opentelemetry/nostd/mpark/variant.h | 1281 ++++++++++++++++ api/include/opentelemetry/nostd/shared_ptr.h | 25 +- api/include/opentelemetry/nostd/span.h | 34 +- api/include/opentelemetry/nostd/string_view.h | 44 +- api/include/opentelemetry/nostd/type_traits.h | 34 +- api/include/opentelemetry/nostd/unique_ptr.h | 28 +- api/include/opentelemetry/nostd/utility.h | 31 +- api/include/opentelemetry/nostd/variant.h | 1294 +---------------- api/include/opentelemetry/std/shared_ptr.h | 31 + api/include/opentelemetry/std/span.h | 76 + api/include/opentelemetry/std/string_view.h | 36 + api/include/opentelemetry/std/type_traits.h | 31 + api/include/opentelemetry/std/unique_ptr.h | 31 + api/include/opentelemetry/std/utility.h | 80 + api/include/opentelemetry/std/variant.h | 235 +++ api/include/opentelemetry/trace/span.h | 11 + api/test/context/CMakeLists.txt | 5 +- api/test/core/CMakeLists.txt | 5 +- api/test/metrics/CMakeLists.txt | 5 +- api/test/nostd/CMakeLists.txt | 5 +- api/test/nostd/shared_ptr_test.cc | 9 +- api/test/nostd/span_test.cc | 17 +- api/test/nostd/string_view_test.cc | 4 +- api/test/plugin/CMakeLists.txt | 5 +- api/test/trace/CMakeLists.txt | 5 +- api/test/trace/propagation/CMakeLists.txt | 5 +- ci/do_ci.sh | 3 + docker/ubuntu14.04/Dockerfile | 2 +- docker/ubuntu16.04/Dockerfile | 2 +- docker/ubuntu18.04/Dockerfile | 2 +- docker/ubuntu20.04/Dockerfile | 2 +- docs/abi-compatibility.md | 35 + docs/building-with-stdlib.md | 191 +++ examples/batch/CMakeLists.txt | 5 +- examples/metrics_simple/CMakeLists.txt | 2 +- examples/otlp/CMakeLists.txt | 11 +- examples/plugin/plugin/tracer.cc | 1 - exporters/ostream/CMakeLists.txt | 4 +- exporters/otlp/src/recordable.cc | 6 + .../ext/http/server/socket_tools.h | 1 - .../sdk/logs/simple_log_processor.h | 2 +- .../opentelemetry/sdk/trace/attribute_utils.h | 30 +- .../sdk/trace/simple_processor.h | 2 +- .../opentelemetry/sdk/trace/span_data.h | 7 +- sdk/src/common/BUILD | 5 +- sdk/src/common/CMakeLists.txt | 5 +- sdk/src/common/core.cc | 12 + sdk/src/trace/CMakeLists.txt | 3 +- sdk/test/common/CMakeLists.txt | 12 +- sdk/test/trace/CMakeLists.txt | 11 +- third_party/benchmark | 2 +- third_party/opentelemetry-proto | 2 +- tools/WORKSPACE | 4 + tools/format.sh | 2 +- tools/install.sh | 0 tools/setup-ninja.sh | 4 + 64 files changed, 2537 insertions(+), 1401 deletions(-) create mode 100644 api/include/opentelemetry/nostd/mpark/variant.h create mode 100644 api/include/opentelemetry/std/shared_ptr.h create mode 100644 api/include/opentelemetry/std/span.h create mode 100644 api/include/opentelemetry/std/string_view.h create mode 100644 api/include/opentelemetry/std/type_traits.h create mode 100644 api/include/opentelemetry/std/unique_ptr.h create mode 100644 api/include/opentelemetry/std/utility.h create mode 100644 api/include/opentelemetry/std/variant.h create mode 100644 docs/abi-compatibility.md create mode 100644 docs/building-with-stdlib.md create mode 100644 sdk/src/common/core.cc create mode 100644 tools/WORKSPACE mode change 100644 => 100755 tools/install.sh create mode 100755 tools/setup-ninja.sh diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1cb9e79419..538fe07f8f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -163,6 +163,16 @@ jobs: - name: run tests run: ./ci/do_ci.sh bazel.tsan + bazel_osx: + name: Bazel on MacOS + runs-on: macos-latest + steps: + - uses: actions/checkout@v2 + with: + submodules: 'recursive' + - name: run tests + run: ./ci/do_ci.sh bazel.test + benchmark: name: Benchmark runs-on: ubuntu-latest @@ -195,15 +205,6 @@ jobs: - name: run tests run: ./ci/do_ci.sh format - osx_test: - name: Bazel on MacOS - runs-on: macos-latest - steps: - - uses: actions/checkout@v2 - with: - submodules: 'recursive' - - name: run tests - run: ./ci/do_ci.sh bazel.test windows: name: CMake -> exporter proto diff --git a/CMakeLists.txt b/CMakeLists.txt index c67451ec01..61db8e332b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,6 +10,48 @@ if(NOT DEFINED CMAKE_CXX_STANDARD) set(CMAKE_CXX_STANDARD 11) endif() +option(WITH_STL "Whether to use Standard Library for C++latest features" OFF) + +option(WITH_ABSEIL "Whether to use Abseil for C++latest features" OFF) + +if(WITH_ABSEIL) + add_definitions(-DHAVE_ABSEIL) + find_package(absl CONFIG REQUIRED) + + # Abseil headers-only lib is needed for absl::variant to work vs2015. + # `nostd::mpark::variant` is not compiling in vs2015. + set(CORE_RUNTIME_LIBS absl::any absl::base absl::bits absl::city) + + # target_link_libraries(main PRIVATE absl::any absl::base absl::bits + # absl::city) +endif() + +if(WITH_STL) + # Require at least C++17. C++20 is needed to avoid gsl::span + add_definitions(-DHAVE_CPP_STDLIB -DHAVE_GSL) + + if(CMAKE_MINOR_VERSION VERSION_GREATER "3.18") + # Ask for 20, may get anything below + set(CMAKE_CXX_STANDARD 20) + else() + # Ask for 17, may get anything below + set(CMAKE_CXX_STANDARD 17) + endif() + + # Guidelines Support Library path. Used if we are not on not get C++20. + # + # TODO: respect WITH_ABSEIL as alternate implementation of std::span + set(GSL_DIR third_party/ms-gsl) + include_directories(${GSL_DIR}/include) + + # Optimize for speed to reduce the hops + if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") + set(CMAKE_CXX_FLAGS_SPEED "/O2") + set(CMAKE_CXX_FLAGS + "${CMAKE_CXX_FLAGS} /Zc:__cplusplus ${CMAKE_CXX_FLAGS_SPEED}") + endif() +endif() + option(WITH_OTLP "Whether to include the OpenTelemetry Protocol in the SDK" OFF) option(WITH_PROMETHEUS "Whether to include the Prometheus Client in the SDK" diff --git a/CMakeSettings.json b/CMakeSettings.json index 4b2923f3ca..e5285d3fad 100644 --- a/CMakeSettings.json +++ b/CMakeSettings.json @@ -46,6 +46,63 @@ "type": "BOOL" } ] + }, + { + "name": "stdlib-x64-Debug", + "generator": "Ninja", + "configurationType": "Debug", + "inheritEnvironments": [ "msvc_x64_x64" ], + "buildRoot": "${projectDir}\\out\\vs2019\\${name}", + "installRoot": "${projectDir}\\out\\vs2019\\${name}\\install", + "cmakeCommandArgs": "", + "buildCommandArgs": "", + "ctestCommandArgs": "", + "variables": [ + { + "name": "WITH_STL", + "value": "True", + "type": "BOOL" + }, + { + "name": "WITH_OTLP", + "value": "True", + "type": "BOOL" + }, + { + "name": "WITH_EXAMPLES", + "value": "true", + "type": "BOOL" + } + ] + }, + { + "name": "stdlib-x64-Release", + "generator": "Ninja", + "configurationType": "RelWithDebInfo", + "inheritEnvironments": [ "msvc_x64_x64" ], + "buildRoot": "${projectDir}\\out\\vs2019\\${name}", + "installRoot": "${projectDir}\\out\\vs2019\\${name}\\install", + "cmakeCommandArgs": "", + "buildCommandArgs": "", + "ctestCommandArgs": "", + "cmakeToolchain": "", + "variables": [ + { + "name": "WITH_STL", + "value": "True", + "type": "BOOL" + }, + { + "name": "WITH_OTLP", + "value": "True", + "type": "BOOL" + }, + { + "name": "WITH_EXAMPLES", + "value": "true", + "type": "BOOL" + } + ] } ] } \ No newline at end of file diff --git a/api/CMakeLists.txt b/api/CMakeLists.txt index e446300a03..2f6eed51fb 100644 --- a/api/CMakeLists.txt +++ b/api/CMakeLists.txt @@ -12,3 +12,9 @@ install( if(BUILD_TESTING) add_subdirectory(test) endif() + +if(WITH_STL) + message("Building with standard library types...") +else() + message("Building with nostd types...") +endif() diff --git a/api/include/opentelemetry/common/attribute_value.h b/api/include/opentelemetry/common/attribute_value.h index e68b6d8bb1..6cb399842a 100644 --- a/api/include/opentelemetry/common/attribute_value.h +++ b/api/include/opentelemetry/common/attribute_value.h @@ -10,20 +10,30 @@ OPENTELEMETRY_BEGIN_NAMESPACE namespace common { -using AttributeValue = nostd::variant, - nostd::span, - nostd::span, - nostd::span, - nostd::span, - nostd::span, - nostd::span>; +using AttributeValue = + nostd::variant, +#endif + nostd::span, + nostd::span, + nostd::span, + nostd::span, + nostd::span, + nostd::span, + nostd::span>; enum AttributeType { @@ -34,8 +44,12 @@ enum AttributeType TYPE_UINT64, TYPE_DOUBLE, TYPE_STRING, +#ifdef HAVE_CSTRING_TYPE TYPE_CSTRING, - // TYPE_SPAN_BYTE, // TODO: not part of OT spec yet +#endif +#ifdef HAVE_SPAN_BYTE + TYPE_SPAN_BYTE, +#endif TYPE_SPAN_BOOL, TYPE_SPAN_INT, TYPE_SPAN_INT64, diff --git a/api/include/opentelemetry/common/key_value_iterable.h b/api/include/opentelemetry/common/key_value_iterable.h index f4e4a92bc3..0742082e56 100644 --- a/api/include/opentelemetry/common/key_value_iterable.h +++ b/api/include/opentelemetry/common/key_value_iterable.h @@ -30,5 +30,23 @@ class KeyValueIterable */ virtual size_t size() const noexcept = 0; }; + +// +// NULL object pattern empty iterable. +// +class NullKeyValueIterable : public KeyValueIterable +{ +public: + NullKeyValueIterable(){}; + + virtual bool ForEachKeyValue( + nostd::function_ref) const noexcept + { + return true; + }; + + virtual size_t size() const noexcept { return 0; } +}; + } // namespace common OPENTELEMETRY_END_NAMESPACE diff --git a/api/include/opentelemetry/logs/logger.h b/api/include/opentelemetry/logs/logger.h index c7e4896165..53aa6e872d 100644 --- a/api/include/opentelemetry/logs/logger.h +++ b/api/include/opentelemetry/logs/logger.h @@ -28,6 +28,7 @@ #include "opentelemetry/nostd/shared_ptr.h" #include "opentelemetry/nostd/span.h" #include "opentelemetry/nostd/string_view.h" +#include "opentelemetry/nostd/type_traits.h" #include "opentelemetry/trace/span_id.h" #include "opentelemetry/trace/trace_flags.h" #include "opentelemetry/trace/trace_id.h" diff --git a/api/include/opentelemetry/nostd/function_ref.h b/api/include/opentelemetry/nostd/function_ref.h index de61c7040a..508cc53029 100644 --- a/api/include/opentelemetry/nostd/function_ref.h +++ b/api/include/opentelemetry/nostd/function_ref.h @@ -1,3 +1,17 @@ +// Copyright 2020, OpenTelemetry 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 +// +// http://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. + #pragma once #include diff --git a/api/include/opentelemetry/nostd/mpark/variant.h b/api/include/opentelemetry/nostd/mpark/variant.h new file mode 100644 index 0000000000..097bb57355 --- /dev/null +++ b/api/include/opentelemetry/nostd/mpark/variant.h @@ -0,0 +1,1281 @@ +// MPark.Variant +// +// Copyright Michael Park, 2015-2017 +// Copyright OpenTelemetry Authors, 2020 +// +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file third_party/boost/LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) + +#pragma once + +#include +#include +#include + +#include "opentelemetry/nostd/detail/all.h" +#include "opentelemetry/nostd/detail/dependent_type.h" +#include "opentelemetry/nostd/detail/find_index.h" +#include "opentelemetry/nostd/detail/functional.h" +#include "opentelemetry/nostd/detail/recursive_union.h" +#include "opentelemetry/nostd/detail/trait.h" +#include "opentelemetry/nostd/detail/type_pack_element.h" +#include "opentelemetry/nostd/detail/variant_alternative.h" +#include "opentelemetry/nostd/detail/variant_fwd.h" +#include "opentelemetry/nostd/detail/variant_size.h" +#include "opentelemetry/nostd/type_traits.h" +#include "opentelemetry/nostd/utility.h" +#include "opentelemetry/version.h" + +#define AUTO_RETURN(...) \ + ->decay_t { return __VA_ARGS__; } + +#define AUTO_REFREF_RETURN(...) \ + ->decltype((__VA_ARGS__)) \ + { \ + static_assert(std::is_reference::value, ""); \ + return __VA_ARGS__; \ + } + +#define DECLTYPE_AUTO_RETURN(...) \ + ->decltype(__VA_ARGS__) { return __VA_ARGS__; } + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace nostd +{ +constexpr std::size_t variant_npos = static_cast(-1); + +class bad_variant_access : public std::exception +{ +public: + virtual const char *what() const noexcept override { return "bad_variant_access"; } +}; + +[[noreturn]] inline void throw_bad_variant_access() +{ +#if __EXCEPTIONS + throw bad_variant_access{}; +#else + std::terminate(); +#endif +} + +namespace detail +{ +namespace access +{ +struct base +{ + template + inline static constexpr auto get_alt(V &&v) +#ifdef _MSC_VER + AUTO_REFREF_RETURN(recursive_union::get_alt(std::forward(v).data_, in_place_index_t{})) +#else + AUTO_REFREF_RETURN(recursive_union::get_alt(data(std::forward(v)), in_place_index_t{})) +#endif +}; + +struct variant +{ + template + inline static constexpr auto get_alt(V &&v) + AUTO_REFREF_RETURN(base::get_alt(std::forward(v).impl_)) +}; +} // namespace access +} // namespace detail + +namespace detail +{ +namespace visitation +{ + +struct base +{ + template + using dispatch_result_t = decltype( + nostd::invoke(std::declval(), access::base::get_alt<0>(std::declval())...)); + + template + struct expected + { + template + inline static constexpr bool but_got() + { + return std::is_same::value; + } + }; + + template + struct visit_return_type_check + { + static_assert(expected::template but_got(), + "`visit` requires the visitor to have a single return type"); + + template + inline static constexpr auto invoke(Visitor &&visitor, Alts &&... alts) + DECLTYPE_AUTO_RETURN(nostd::invoke(std::forward(visitor), + std::forward(alts)...)) + }; + + template + inline static constexpr const T &at(const T &elem) noexcept + { + return elem; + } + + template + inline static constexpr const remove_all_extents_t &at(const std::array &elems, + std::size_t i, + Is... is) noexcept + { + return at(elems[i], is...); + } + + template + inline static constexpr std::array, sizeof...(Fs) + 1> make_farray(F &&f, Fs &&... fs) + { + return {{std::forward(f), std::forward(fs)...}}; + } + + template + struct make_fmatrix_impl + { + + template + inline static constexpr dispatch_result_t dispatch(F &&f, Vs &&... vs) + { + using Expected = dispatch_result_t; + using Actual = decltype( + nostd::invoke(std::forward(f), access::base::get_alt(std::forward(vs))...)); + return visit_return_type_check::invoke( + std::forward(f), access::base::get_alt(std::forward(vs))...); + } + + template + struct impl; + + template + struct impl> + { + inline constexpr auto operator()() const AUTO_RETURN(&dispatch) + }; + + template + struct impl, Ls...> + { + inline constexpr auto operator()() const + AUTO_RETURN(make_farray(impl, Ls...>{}()...)) + }; + }; + + template + inline static constexpr auto make_fmatrix() AUTO_RETURN( + typename make_fmatrix_impl:: + template impl, make_index_sequence::size()>...>{}()) + + template + struct make_fdiagonal_impl + { + template + inline static constexpr dispatch_result_t dispatch(F &&f, Vs &&... vs) + { + using Expected = dispatch_result_t; + using Actual = decltype( + nostd::invoke(std::forward(f), access::base::get_alt(std::forward(vs))...)); + return visit_return_type_check::invoke( + std::forward(f), access::base::get_alt(std::forward(vs))...); + } + + template + inline static constexpr auto impl(index_sequence) + AUTO_RETURN(make_farray(&dispatch...)) + }; + + template + inline static constexpr auto make_fdiagonal() + -> decltype(make_fdiagonal_impl::impl(make_index_sequence::size()>{})) + { + static_assert(all<(decay_t::size() == decay_t::size())...>::value, + "all of the variants must be the same size."); + return make_fdiagonal_impl::impl(make_index_sequence::size()>{}); + } +}; + +#if !defined(_MSC_VER) || _MSC_VER >= 1910 +template +using fmatrix_t = decltype(base::make_fmatrix()); + +template +struct fmatrix +{ + static constexpr fmatrix_t value = base::make_fmatrix(); +}; + +template +constexpr fmatrix_t fmatrix::value; + +template +using fdiagonal_t = decltype(base::make_fdiagonal()); + +template +struct fdiagonal +{ + static constexpr fdiagonal_t value = base::make_fdiagonal(); +}; + +template +constexpr fdiagonal_t fdiagonal::value; +#endif + +struct alt +{ + template + inline static constexpr auto visit_alt(Visitor &&visitor, Vs &&... vs) DECLTYPE_AUTO_RETURN( + base::at(base::make_fmatrix(vs)))...>(), + vs.index()...)(std::forward(visitor), as_base(std::forward(vs))...)) + + template + inline static constexpr auto visit_alt_at(std::size_t index, Visitor &&visitor, Vs &&... vs) + DECLTYPE_AUTO_RETURN(base::at( + base::make_fdiagonal(vs)))...>(), + index)(std::forward(visitor), as_base(std::forward(vs))...)) +}; + +struct variant +{ +private: + template + struct visitor + { + template + inline static constexpr bool does_not_handle() + { + return nostd::is_invocable::value; + } + }; + + template + struct visit_exhaustiveness_check + { + static_assert(visitor::template does_not_handle(), + "`visit` requires the visitor to be exhaustive."); + + inline static constexpr auto invoke(Visitor &&visitor, Values &&... values) + DECLTYPE_AUTO_RETURN(nostd::invoke(std::forward(visitor), + std::forward(values)...)) + }; + + template + struct value_visitor + { + Visitor &&visitor_; + + template + inline constexpr auto operator()(Alts &&... alts) const DECLTYPE_AUTO_RETURN( + visit_exhaustiveness_check(alts).value))...>::invoke( + std::forward(visitor_), + std::forward(alts).value...)) + }; + + template + inline static constexpr auto make_value_visitor(Visitor &&visitor) + AUTO_RETURN(value_visitor{std::forward(visitor)}) + + public + : template + inline static constexpr auto visit_alt(Visitor &&visitor, Vs &&... vs) + DECLTYPE_AUTO_RETURN(alt::visit_alt(std::forward(visitor), + std::forward(vs).impl_...)) + + template + inline static constexpr auto visit_alt_at(std::size_t index, + Visitor &&visitor, + Vs &&... vs) + DECLTYPE_AUTO_RETURN(alt::visit_alt_at(index, + std::forward(visitor), + std::forward(vs).impl_...)) + + template + inline static constexpr auto visit_value(Visitor &&visitor, Vs &&... vs) + DECLTYPE_AUTO_RETURN( + visit_alt(make_value_visitor(std::forward(visitor)), + std::forward(vs)...)) + + template + inline static constexpr auto visit_value_at(std::size_t index, + Visitor &&visitor, + Vs &&... vs) + DECLTYPE_AUTO_RETURN(visit_alt_at( + index, + make_value_visitor(std::forward(visitor)), + std::forward(vs)...)) +}; +} // namespace visitation + +template +using index_t = typename std::conditional< + sizeof...(Ts) < (std::numeric_limits::max)(), + unsigned char, + typename std::conditional::max)(), + unsigned short, + unsigned int>::type>::type; + +template +class base +{ +public: + inline explicit constexpr base(valueless_t tag) noexcept + : data_(tag), index_(static_cast>(-1)) + {} + + template + inline explicit constexpr base(in_place_index_t, Args &&... args) + : data_(in_place_index_t{}, std::forward(args)...), index_(I) + {} + + inline constexpr bool valueless_by_exception() const noexcept + { + return index_ == static_cast>(-1); + } + + inline constexpr std::size_t index() const noexcept + { + return valueless_by_exception() ? variant_npos : index_; + } + +protected: + using data_t = recursive_union; + + friend inline constexpr base &as_base(base &b) { return b; } + friend inline constexpr const base &as_base(const base &b) { return b; } + friend inline constexpr base &&as_base(base &&b) { return std::move(b); } + friend inline constexpr const base &&as_base(const base &&b) { return std::move(b); } + + friend inline constexpr data_t &data(base &b) { return b.data_; } + friend inline constexpr const data_t &data(const base &b) { return b.data_; } + friend inline constexpr data_t &&data(base &&b) { return std::move(b).data_; } + friend inline constexpr const data_t &&data(const base &&b) { return std::move(b).data_; } + + inline static constexpr std::size_t size() { return sizeof...(Ts); } + + data_t data_; + index_t index_; + + friend struct access::base; + friend struct visitation::base; +}; + +struct dtor +{ +#ifdef _MSC_VER +# pragma warning(push) +# pragma warning(disable : 4100) +#endif + template + inline void operator()(Alt &alt) const noexcept + { + alt.~Alt(); + } +#ifdef _MSC_VER +# pragma warning(pop) +#endif +}; + +template +class destructor; + +#define OPENTELEMETRY_VARIANT_DESTRUCTOR(destructible_trait, definition, destroy) \ + template \ + class destructor, destructible_trait> : public base \ + { \ + using super = base; \ + \ + public: \ + using super::super; \ + using super::operator=; \ + \ + destructor(const destructor &) = default; \ + destructor(destructor &&) = default; \ + definition destructor &operator=(const destructor &) = default; \ + destructor &operator=(destructor &&) = default; \ + \ + protected: \ + destroy \ + } + +OPENTELEMETRY_VARIANT_DESTRUCTOR( + Trait::TriviallyAvailable, ~destructor() = default; + , inline void destroy() noexcept { this->index_ = static_cast>(-1); }); + +OPENTELEMETRY_VARIANT_DESTRUCTOR( + Trait::Available, + ~destructor() { destroy(); }, + inline void destroy() noexcept { + if (!this->valueless_by_exception()) + { + visitation::alt::visit_alt(dtor{}, *this); + } + this->index_ = static_cast>(-1); + }); + +OPENTELEMETRY_VARIANT_DESTRUCTOR(Trait::Unavailable, ~destructor() = delete; + , inline void destroy() noexcept = delete;); + +#undef OPENTELEMETRY_VARIANT_DESTRUCTOR + +template +class constructor : public destructor +{ + using super = destructor; + +public: + using super::super; + using super::operator=; + +protected: + struct ctor + { + template + inline void operator()(LhsAlt &lhs_alt, RhsAlt &&rhs_alt) const + { + constructor::construct_alt(lhs_alt, std::forward(rhs_alt).value); + } + }; + + template + inline static T &construct_alt(alt &a, Args &&... args) + { + auto *result = ::new (static_cast(std::addressof(a))) + alt(in_place_t{}, std::forward(args)...); + return result->value; + } + + template + inline static void generic_construct(constructor &lhs, Rhs &&rhs) + { + lhs.destroy(); + if (!rhs.valueless_by_exception()) + { + visitation::alt::visit_alt_at(rhs.index(), ctor{}, lhs, std::forward(rhs)); + lhs.index_ = rhs.index_; + } + } +}; + +template +class move_constructor; + +#define OPENTELEMETRY_VARIANT_MOVE_CONSTRUCTOR(move_constructible_trait, definition) \ + template \ + class move_constructor, move_constructible_trait> \ + : public constructor> \ + { \ + using super = constructor>; \ + \ + public: \ + using super::super; \ + using super::operator=; \ + \ + move_constructor(const move_constructor &) = default; \ + definition ~move_constructor() = default; \ + move_constructor &operator=(const move_constructor &) = default; \ + move_constructor &operator=(move_constructor &&) = default; \ + } + +OPENTELEMETRY_VARIANT_MOVE_CONSTRUCTOR(Trait::TriviallyAvailable, + move_constructor(move_constructor &&that) = default;); + +OPENTELEMETRY_VARIANT_MOVE_CONSTRUCTOR( + Trait::Available, + move_constructor(move_constructor &&that) noexcept( + all::value...>::value) + : move_constructor(valueless_t{}) { this->generic_construct(*this, std::move(that)); }); + +OPENTELEMETRY_VARIANT_MOVE_CONSTRUCTOR(Trait::Unavailable, + move_constructor(move_constructor &&) = delete;); + +#undef OPENTELEMETRY_VARIANT_MOVE_CONSTRUCTOR + +template +class copy_constructor; + +#define OPENTELEMETRY_VARIANT_COPY_CONSTRUCTOR(copy_constructible_trait, definition) \ + template \ + class copy_constructor, copy_constructible_trait> \ + : public move_constructor> \ + { \ + using super = move_constructor>; \ + \ + public: \ + using super::super; \ + using super::operator=; \ + \ + definition copy_constructor(copy_constructor &&) = default; \ + ~copy_constructor() = default; \ + copy_constructor &operator=(const copy_constructor &) = default; \ + copy_constructor &operator=(copy_constructor &&) = default; \ + } + +OPENTELEMETRY_VARIANT_COPY_CONSTRUCTOR(Trait::TriviallyAvailable, + copy_constructor(const copy_constructor &that) = default;); + +OPENTELEMETRY_VARIANT_COPY_CONSTRUCTOR( + Trait::Available, copy_constructor(const copy_constructor &that) + : copy_constructor(valueless_t{}) { this->generic_construct(*this, that); }); + +OPENTELEMETRY_VARIANT_COPY_CONSTRUCTOR(Trait::Unavailable, + copy_constructor(const copy_constructor &) = delete;); + +#undef OPENTELEMETRY_VARIANT_COPY_CONSTRUCTOR + +template +class assignment : public copy_constructor +{ + using super = copy_constructor; + +public: + using super::super; + using super::operator=; + + template + inline /* auto & */ auto emplace(Args &&... args) + -> decltype(this->construct_alt(access::base::get_alt(*this), std::forward(args)...)) + { + this->destroy(); + auto &result = + this->construct_alt(access::base::get_alt(*this), std::forward(args)...); + this->index_ = I; + return result; + } + +protected: + template + struct assigner + { + template + inline void operator()(ThisAlt &this_alt, ThatAlt &&that_alt) const + { + self->assign_alt(this_alt, std::forward(that_alt).value); + } + assignment *self; + }; + + template + inline void assign_alt(alt &a, Arg &&arg) + { + if (this->index() == I) + { +#ifdef _MSC_VER +# pragma warning(push) +# pragma warning(disable : 4244) +#endif + a.value = std::forward(arg); +#ifdef _MSC_VER +# pragma warning(pop) +#endif + } + else + { + struct + { + void operator()(std::true_type) const { this_->emplace(std::forward(arg_)); } + void operator()(std::false_type) const { this_->emplace(T(std::forward(arg_))); } + assignment *this_; + Arg &&arg_; + } impl{this, std::forward(arg)}; + impl(bool_constant < std::is_nothrow_constructible::value || + !std::is_nothrow_move_constructible::value > {}); + } + } + + template + inline void generic_assign(That &&that) + { + if (this->valueless_by_exception() && that.valueless_by_exception()) + { + // do nothing. + } + else if (that.valueless_by_exception()) + { + this->destroy(); + } + else + { + visitation::alt::visit_alt_at(that.index(), assigner{this}, *this, + std::forward(that)); + } + } +}; + +template +class move_assignment; + +#define OPENTELEMETRY_VARIANT_MOVE_ASSIGNMENT(move_assignable_trait, definition) \ + template \ + class move_assignment, move_assignable_trait> : public assignment> \ + { \ + using super = assignment>; \ + \ + public: \ + using super::super; \ + using super::operator=; \ + \ + move_assignment(const move_assignment &) = default; \ + move_assignment(move_assignment &&) = default; \ + ~move_assignment() = default; \ + move_assignment &operator=(const move_assignment &) = default; \ + definition \ + } + +OPENTELEMETRY_VARIANT_MOVE_ASSIGNMENT( + Trait::TriviallyAvailable, move_assignment &operator=(move_assignment &&that) = default;); + +OPENTELEMETRY_VARIANT_MOVE_ASSIGNMENT( + Trait::Available, + move_assignment & + operator=(move_assignment &&that) noexcept( + all<(std::is_nothrow_move_constructible::value && + std::is_nothrow_move_assignable::value)...>::value) { + this->generic_assign(std::move(that)); + return *this; + }); + +OPENTELEMETRY_VARIANT_MOVE_ASSIGNMENT(Trait::Unavailable, + move_assignment &operator=(move_assignment &&) = delete;); + +#undef OPENTELEMETRY_VARIANT_MOVE_ASSIGNMENT + +template +class copy_assignment; + +#define OPENTELEMETRY_VARIANT_COPY_ASSIGNMENT(copy_assignable_trait, definition) \ + template \ + class copy_assignment, copy_assignable_trait> \ + : public move_assignment> \ + { \ + using super = move_assignment>; \ + \ + public: \ + using super::super; \ + using super::operator=; \ + \ + copy_assignment(const copy_assignment &) = default; \ + copy_assignment(copy_assignment &&) = default; \ + ~copy_assignment() = default; \ + definition copy_assignment &operator=(copy_assignment &&) = default; \ + } + +OPENTELEMETRY_VARIANT_COPY_ASSIGNMENT( + Trait::TriviallyAvailable, copy_assignment &operator=(const copy_assignment &that) = default;); + +OPENTELEMETRY_VARIANT_COPY_ASSIGNMENT( + Trait::Available, + copy_assignment & + operator=(const copy_assignment &that) { + this->generic_assign(that); + return *this; + }); + +OPENTELEMETRY_VARIANT_COPY_ASSIGNMENT( + Trait::Unavailable, copy_assignment &operator=(const copy_assignment &) = delete;); + +#undef OPENTELEMETRY_VARIANT_COPY_ASSIGNMENT +template +class impl : public copy_assignment> +{ + using super = copy_assignment>; + +public: + using super::super; + using super::operator=; + + impl(const impl &) = default; + impl(impl &&) = default; + ~impl() = default; + impl &operator=(const impl &) = default; + impl &operator=(impl &&) = default; + + template + inline void assign(Arg &&arg) + { + this->assign_alt(access::base::get_alt(*this), std::forward(arg)); + } + + inline void swap(impl &that) + { + if (this->valueless_by_exception() && that.valueless_by_exception()) + { + // do nothing. + } + else if (this->index() == that.index()) + { + visitation::alt::visit_alt_at(this->index(), swapper{}, *this, that); + } + else + { + impl *lhs = this; + impl *rhs = std::addressof(that); + if (lhs->move_nothrow() && !rhs->move_nothrow()) + { + std::swap(lhs, rhs); + } + impl tmp(std::move(*rhs)); +#if __EXCEPTIONS + // EXTENSION: When the move construction of `lhs` into `rhs` throws + // and `tmp` is nothrow move constructible then we move `tmp` back + // into `rhs` and provide the strong exception safety guarantee. + try + { + this->generic_construct(*rhs, std::move(*lhs)); + } + catch (...) + { + if (tmp.move_nothrow()) + { + this->generic_construct(*rhs, std::move(tmp)); + } + throw; + } +#else + this->generic_construct(*rhs, std::move(*lhs)); +#endif + this->generic_construct(*lhs, std::move(tmp)); + } + } + +private: + struct swapper + { + template + inline void operator()(ThisAlt &this_alt, ThatAlt &that_alt) const + { + using std::swap; + swap(this_alt.value, that_alt.value); + } + }; + + inline constexpr bool move_nothrow() const + { + return this->valueless_by_exception() || + std::array{ + {std::is_nothrow_move_constructible::value...}}[this->index()]; + } +}; + +template +struct is_non_narrowing_convertible +{ + template + static std::true_type test(T(&&)[1]); + + template + static auto impl(int) -> decltype(test({std::declval()})); + + template + static auto impl(...) -> std::false_type; + + static constexpr bool value = decltype(impl(0))::value; +}; + +template ::value, + typename = void> +struct overload_leaf +{}; + +template +struct overload_leaf +{ + using impl = size_constant (*)(T); + operator impl() const { return nullptr; }; +}; + +template +struct overload_leaf, bool>::value + ? std::is_same, bool>::value + : +#if defined(__clang__) || !defined(__GNUC__) || __GNUC__ >= 5 + is_non_narrowing_convertible::value +#else + std::is_convertible::value +#endif + >> +{ + using impl = size_constant (*)(T); + operator impl() const { return nullptr; }; +}; + +template +struct overload_impl +{ +private: + template + struct impl; + + template + struct impl> : overload_leaf... + {}; + +public: + using type = impl>; +}; + +template +using overload = typename overload_impl::type; + +template +using best_match = invoke_result_t, Arg>; + +template +struct is_in_place_index : std::false_type +{}; + +template +struct is_in_place_index> : std::true_type +{}; + +template +struct is_in_place_type : std::false_type +{}; + +template +struct is_in_place_type> : std::true_type +{}; +} // namespace detail + +template +class variant +{ + static_assert(0 < sizeof...(Ts), "variant must consist of at least one alternative."); + + static_assert(detail::all::value...>::value, + "variant can not have an array type as an alternative."); + + static_assert(detail::all::value...>::value, + "variant can not have a reference type as an alternative."); + + static_assert(detail::all::value...>::value, + "variant can not have a void type as an alternative."); + +public: + template , + enable_if_t::value, int> = 0> + inline constexpr variant() noexcept(std::is_nothrow_default_constructible::value) + : impl_(in_place_index_t<0>{}) + {} + + variant(const variant &) = default; + variant(variant &&) = default; + + template , + enable_if_t::value, int> = 0, + enable_if_t::value, int> = 0, + enable_if_t::value, int> = 0, + std::size_t I = detail::best_match::value, + typename T = detail::type_pack_element_t, + enable_if_t::value, int> = 0> + inline constexpr variant(Arg &&arg) noexcept(std::is_nothrow_constructible::value) + : impl_(in_place_index_t{}, std::forward(arg)) + {} + + template , + enable_if_t::value, int> = 0> + inline explicit constexpr variant(in_place_index_t, Args &&... args) noexcept( + std::is_nothrow_constructible::value) + : impl_(in_place_index_t{}, std::forward(args)...) + {} + + template < + std::size_t I, + typename Up, + typename... Args, + typename T = detail::type_pack_element_t, + enable_if_t &, Args...>::value, int> = 0> + inline explicit constexpr variant( + in_place_index_t, + std::initializer_list il, + Args &&... args) noexcept(std::is_nothrow_constructible &, + Args...>::value) + : impl_(in_place_index_t{}, il, std::forward(args)...) + {} + + template ::value, + enable_if_t::value, int> = 0> + inline explicit constexpr variant(in_place_type_t, Args &&... args) noexcept( + std::is_nothrow_constructible::value) + : impl_(in_place_index_t{}, std::forward(args)...) + {} + + template < + typename T, + typename Up, + typename... Args, + std::size_t I = detail::find_index_sfinae::value, + enable_if_t &, Args...>::value, int> = 0> + inline explicit constexpr variant( + in_place_type_t, + std::initializer_list il, + Args &&... args) noexcept(std::is_nothrow_constructible &, + Args...>::value) + : impl_(in_place_index_t{}, il, std::forward(args)...) + {} + + ~variant() = default; + + variant &operator=(const variant &) = default; + variant &operator=(variant &&) = default; + + template < + typename Arg, + enable_if_t, variant>::value, int> = 0, + std::size_t I = detail::best_match::value, + typename T = detail::type_pack_element_t, + enable_if_t<(std::is_assignable::value && std::is_constructible::value), + int> = 0> + inline variant &operator=(Arg &&arg) noexcept((std::is_nothrow_assignable::value && + std::is_nothrow_constructible::value)) + { + impl_.template assign(std::forward(arg)); + return *this; + } + + template , + enable_if_t::value, int> = 0> + inline T &emplace(Args &&... args) + { + return impl_.template emplace(std::forward(args)...); + } + + template < + std::size_t I, + typename Up, + typename... Args, + typename T = detail::type_pack_element_t, + enable_if_t &, Args...>::value, int> = 0> + inline T &emplace(std::initializer_list il, Args &&... args) + { + return impl_.template emplace(il, std::forward(args)...); + } + + template ::value, + enable_if_t::value, int> = 0> + inline T &emplace(Args &&... args) + { + return impl_.template emplace(std::forward(args)...); + } + + template < + typename T, + typename Up, + typename... Args, + std::size_t I = detail::find_index_sfinae::value, + enable_if_t &, Args...>::value, int> = 0> + inline T &emplace(std::initializer_list il, Args &&... args) + { + return impl_.template emplace(il, std::forward(args)...); + } + + inline constexpr bool valueless_by_exception() const noexcept + { + return impl_.valueless_by_exception(); + } + + inline constexpr std::size_t index() const noexcept { return impl_.index(); } + + template , Dummy>::value && + detail::dependent_type, Dummy>::value)...>::value, + int> = 0> + inline void swap(variant &that) noexcept( + detail::all<(std::is_nothrow_move_constructible::value && + is_nothrow_swappable::value)...>::value) + { + impl_.swap(that.impl_); + } + +private: + detail::impl impl_; + + friend struct detail::access::variant; + friend struct detail::visitation::variant; +}; + +template +inline constexpr bool holds_alternative(const variant &v) noexcept +{ + return v.index() == I; +} + +template +inline constexpr bool holds_alternative(const variant &v) noexcept +{ + return holds_alternative::value>(v); +} + +namespace detail +{ +template +struct generic_get_impl +{ + constexpr generic_get_impl(int) noexcept {} + + constexpr auto operator()(V &&v) const + AUTO_REFREF_RETURN(access::variant::get_alt(std::forward(v)).value) +}; + +template +inline constexpr auto generic_get(V &&v) AUTO_REFREF_RETURN(generic_get_impl( + holds_alternative(v) ? 0 : (throw_bad_variant_access(), 0))(std::forward(v))) +} // namespace detail + +template +inline constexpr variant_alternative_t> &get(variant &v) +{ + return detail::generic_get(v); +} + +template +inline constexpr variant_alternative_t> &&get(variant &&v) +{ + return detail::generic_get(std::move(v)); +} + +template +inline constexpr const variant_alternative_t> &get(const variant &v) +{ + return detail::generic_get(v); +} + +template +inline constexpr const variant_alternative_t> &&get(const variant &&v) +{ + return detail::generic_get(std::move(v)); +} + +template +inline constexpr T &get(variant &v) +{ + return get::value>(v); +} + +template +inline constexpr T &&get(variant &&v) +{ + return get::value>(std::move(v)); +} + +template +inline constexpr const T &get(const variant &v) +{ + return get::value>(v); +} + +template +inline constexpr const T &&get(const variant &&v) +{ + return get::value>(std::move(v)); +} + +namespace detail +{ + +template +inline constexpr /* auto * */ auto generic_get_if(V *v) noexcept AUTO_RETURN( + v &&holds_alternative(*v) ? std::addressof(access::variant::get_alt(*v).value) : nullptr) + +} // namespace detail + +template +inline constexpr add_pointer_t>> get_if( + variant *v) noexcept +{ + return detail::generic_get_if(v); +} + +template +inline constexpr add_pointer_t>> get_if( + const variant *v) noexcept +{ + return detail::generic_get_if(v); +} + +template +inline constexpr add_pointer_t get_if(variant *v) noexcept +{ + return get_if::value>(v); +} + +template +inline constexpr add_pointer_t get_if(const variant *v) noexcept +{ + return get_if::value>(v); +} + +namespace detail +{ +template +struct convert_to_bool +{ + template + inline constexpr bool operator()(Lhs &&lhs, Rhs &&rhs) const + { + static_assert(std::is_convertible, bool>::value, + "relational operators must return a type" + " implicitly convertible to bool"); + return nostd::invoke(RelOp{}, std::forward(lhs), std::forward(rhs)); + } +}; +} // namespace detail + +template +inline constexpr bool operator==(const variant &lhs, const variant &rhs) +{ + using detail::visitation::variant; + using equal_to = detail::convert_to_bool; + return lhs.index() == rhs.index() && (lhs.valueless_by_exception() || + variant::visit_value_at(lhs.index(), equal_to{}, lhs, rhs)); +} + +template +inline constexpr bool operator!=(const variant &lhs, const variant &rhs) +{ + using detail::visitation::variant; + using not_equal_to = detail::convert_to_bool; + return lhs.index() != rhs.index() || + (!lhs.valueless_by_exception() && + variant::visit_value_at(lhs.index(), not_equal_to{}, lhs, rhs)); +} + +template +inline constexpr bool operator<(const variant &lhs, const variant &rhs) +{ + using detail::visitation::variant; + using less = detail::convert_to_bool; + return !rhs.valueless_by_exception() && + (lhs.valueless_by_exception() || lhs.index() < rhs.index() || + (lhs.index() == rhs.index() && variant::visit_value_at(lhs.index(), less{}, lhs, rhs))); +} + +template +inline constexpr bool operator>(const variant &lhs, const variant &rhs) +{ + using detail::visitation::variant; + using greater = detail::convert_to_bool; + return !lhs.valueless_by_exception() && + (rhs.valueless_by_exception() || lhs.index() > rhs.index() || + (lhs.index() == rhs.index() && + variant::visit_value_at(lhs.index(), greater{}, lhs, rhs))); +} + +template +inline constexpr bool operator<=(const variant &lhs, const variant &rhs) +{ + using detail::visitation::variant; + using less_equal = detail::convert_to_bool; + return lhs.valueless_by_exception() || + (!rhs.valueless_by_exception() && + (lhs.index() < rhs.index() || + (lhs.index() == rhs.index() && + variant::visit_value_at(lhs.index(), less_equal{}, lhs, rhs)))); +} + +template +inline constexpr bool operator>=(const variant &lhs, const variant &rhs) +{ + using detail::visitation::variant; + using greater_equal = detail::convert_to_bool; + return rhs.valueless_by_exception() || + (!lhs.valueless_by_exception() && + (lhs.index() > rhs.index() || + (lhs.index() == rhs.index() && + variant::visit_value_at(lhs.index(), greater_equal{}, lhs, rhs)))); +} + +struct monostate +{}; + +inline constexpr bool operator<(monostate, monostate) noexcept +{ + return false; +} + +inline constexpr bool operator>(monostate, monostate) noexcept +{ + return false; +} + +inline constexpr bool operator<=(monostate, monostate) noexcept +{ + return true; +} + +inline constexpr bool operator>=(monostate, monostate) noexcept +{ + return true; +} + +inline constexpr bool operator==(monostate, monostate) noexcept +{ + return true; +} + +inline constexpr bool operator!=(monostate, monostate) noexcept +{ + return false; +} + +namespace detail +{ + +template +inline constexpr bool all_of_impl(const std::array &bs, std::size_t idx) +{ + return idx >= N || (bs[idx] && all_of_impl(bs, idx + 1)); +} + +template +inline constexpr bool all_of(const std::array &bs) +{ + return all_of_impl(bs, 0); +} + +} // namespace detail + +template +inline constexpr auto visit(Visitor &&visitor, Vs &&... vs) DECLTYPE_AUTO_RETURN( + (detail::all_of(std::array{{!vs.valueless_by_exception()...}}) + ? (void)0 + : throw_bad_variant_access()), + detail::visitation::variant::visit_value(std::forward(visitor), + std::forward(vs)...)) template +inline auto swap(variant &lhs, variant &rhs) noexcept(noexcept(lhs.swap(rhs))) + -> decltype(lhs.swap(rhs)) +{ + lhs.swap(rhs); +} +} // namespace nostd +OPENTELEMETRY_END_NAMESPACE + +#undef AUTO_RETURN + +#undef AUTO_REFREF_RETURN + +#undef DECLTYPE_AUTO_RETURN diff --git a/api/include/opentelemetry/nostd/shared_ptr.h b/api/include/opentelemetry/nostd/shared_ptr.h index 7d84b6f408..e7c2a464d2 100644 --- a/api/include/opentelemetry/nostd/shared_ptr.h +++ b/api/include/opentelemetry/nostd/shared_ptr.h @@ -1,8 +1,26 @@ -#pragma once +// Copyright 2020, OpenTelemetry 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 +// +// http://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. -#include +#pragma once +#ifdef HAVE_CPP_STDLIB +# include "opentelemetry/std/shared_ptr.h" +#else +# include +# include +# include -#include "opentelemetry/version.h" +# include "opentelemetry/version.h" OPENTELEMETRY_BEGIN_NAMESPACE namespace nostd @@ -180,3 +198,4 @@ inline bool operator!=(std::nullptr_t, const shared_ptr &rhs) noexcept } } // namespace nostd OPENTELEMETRY_END_NAMESPACE +#endif diff --git a/api/include/opentelemetry/nostd/span.h b/api/include/opentelemetry/nostd/span.h index f28a80cbbf..5b95010edd 100644 --- a/api/include/opentelemetry/nostd/span.h +++ b/api/include/opentelemetry/nostd/span.h @@ -1,14 +1,31 @@ +// Copyright 2020, OpenTelemetry 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 +// +// http://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. + #pragma once -#include -#include -#include -#include -#include -#include +#ifdef HAVE_CPP_STDLIB +# include "opentelemetry/std/span.h" +#else +# include +# include +# include +# include +# include +# include -#include "opentelemetry/nostd/utility.h" -#include "opentelemetry/version.h" +# include "opentelemetry/nostd/utility.h" +# include "opentelemetry/version.h" OPENTELEMETRY_BEGIN_NAMESPACE namespace nostd @@ -238,3 +255,4 @@ class span }; } // namespace nostd OPENTELEMETRY_END_NAMESPACE +#endif diff --git a/api/include/opentelemetry/nostd/string_view.h b/api/include/opentelemetry/nostd/string_view.h index a5d65d00aa..4a5bdefa81 100644 --- a/api/include/opentelemetry/nostd/string_view.h +++ b/api/include/opentelemetry/nostd/string_view.h @@ -1,13 +1,30 @@ +// Copyright 2020, OpenTelemetry 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 +// +// http://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. + #pragma once -#include -#include -#include -#include -#include -#include +#ifdef HAVE_CPP_STDLIB +# include "opentelemetry/std/string_view.h" +#else +# include +# include +# include +# include +# include +# include -#include "opentelemetry/version.h" +# include "opentelemetry/version.h" OPENTELEMETRY_BEGIN_NAMESPACE namespace nostd @@ -58,11 +75,11 @@ class string_view { if (pos > length_) { -#if __EXCEPTIONS +# if __EXCEPTIONS throw std::out_of_range{"opentelemetry::nostd::string_view"}; -#else +# else std::terminate(); -#endif +# endif } n = (std::min)(n, length_ - pos); return string_view(data_ + pos, n); @@ -118,12 +135,12 @@ class string_view inline bool operator==(string_view lhs, string_view rhs) noexcept { return lhs.length() == rhs.length() && -#if _MSC_VER == 1900 +# if _MSC_VER == 1900 // Avoid SCL error in Visual Studio 2015 (std::memcmp(lhs.data(), rhs.data(), lhs.length()) == 0); -#else +# else std::equal(lhs.data(), lhs.data() + lhs.length(), rhs.data()); -#endif +# endif } inline bool operator==(string_view lhs, const std::string &rhs) noexcept @@ -192,3 +209,4 @@ struct hash } }; } // namespace std +#endif diff --git a/api/include/opentelemetry/nostd/type_traits.h b/api/include/opentelemetry/nostd/type_traits.h index a42f440df8..c1e4cfdf02 100644 --- a/api/include/opentelemetry/nostd/type_traits.h +++ b/api/include/opentelemetry/nostd/type_traits.h @@ -1,11 +1,28 @@ +// Copyright 2020, OpenTelemetry 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 +// +// http://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. + #pragma once -#include -#include +#ifdef HAVE_CPP_STDLIB +# include "opentelemetry/std/type_traits.h" +#else +# include +# include -#include "opentelemetry/config.h" -#include "opentelemetry/nostd/detail/void.h" -#include "opentelemetry/version.h" +# include "opentelemetry/config.h" +# include "opentelemetry/nostd/detail/void.h" +# include "opentelemetry/version.h" OPENTELEMETRY_BEGIN_NAMESPACE namespace nostd @@ -116,12 +133,12 @@ using is_nothrow_swappable = detail::swappable::is_nothrow_swappable struct is_trivially_copy_constructible { @@ -145,6 +162,7 @@ struct is_trivially_move_assignable { static constexpr bool value = __is_trivial(T); }; -#endif +# endif } // namespace nostd OPENTELEMETRY_END_NAMESPACE +#endif diff --git a/api/include/opentelemetry/nostd/unique_ptr.h b/api/include/opentelemetry/nostd/unique_ptr.h index fc9a36aa7b..b0418891d0 100644 --- a/api/include/opentelemetry/nostd/unique_ptr.h +++ b/api/include/opentelemetry/nostd/unique_ptr.h @@ -1,11 +1,28 @@ +// Copyright 2020, OpenTelemetry 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 +// +// http://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. + #pragma once -#include -#include -#include -#include +#ifdef HAVE_CPP_STDLIB +# include "opentelemetry/std/unique_ptr.h" +#else +# include +# include +# include +# include -#include "opentelemetry/version.h" +# include "opentelemetry/version.h" OPENTELEMETRY_BEGIN_NAMESPACE namespace nostd @@ -166,3 +183,4 @@ bool operator!=(std::nullptr_t, const unique_ptr &rhs) noexcept } } // namespace nostd OPENTELEMETRY_END_NAMESPACE +#endif diff --git a/api/include/opentelemetry/nostd/utility.h b/api/include/opentelemetry/nostd/utility.h index d7fa3bfc19..ae9197e714 100644 --- a/api/include/opentelemetry/nostd/utility.h +++ b/api/include/opentelemetry/nostd/utility.h @@ -1,12 +1,30 @@ +// Copyright 2020, OpenTelemetry 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 +// +// http://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. + #pragma once -#include -#include -#include +#ifdef HAVE_CPP_STDLIB +# include "opentelemetry/std/utility.h" +#else + +# include +# include +# include -#include "opentelemetry/nostd/detail/decay.h" -#include "opentelemetry/nostd/detail/invoke.h" -#include "opentelemetry/version.h" +# include "opentelemetry/nostd/detail/decay.h" +# include "opentelemetry/nostd/detail/invoke.h" +# include "opentelemetry/version.h" OPENTELEMETRY_BEGIN_NAMESPACE namespace nostd @@ -146,3 +164,4 @@ struct in_place_type_t }; } // namespace nostd OPENTELEMETRY_END_NAMESPACE +#endif diff --git a/api/include/opentelemetry/nostd/variant.h b/api/include/opentelemetry/nostd/variant.h index 097bb57355..37281e1db6 100644 --- a/api/include/opentelemetry/nostd/variant.h +++ b/api/include/opentelemetry/nostd/variant.h @@ -1,1281 +1,33 @@ -// MPark.Variant +// Copyright 2020, OpenTelemetry Authors // -// Copyright Michael Park, 2015-2017 -// Copyright OpenTelemetry Authors, 2020 +// 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 // -// Distributed under the Boost Software License, Version 1.0. -// (See accompanying file third_party/boost/LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) +// http://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. #pragma once -#include -#include -#include - -#include "opentelemetry/nostd/detail/all.h" -#include "opentelemetry/nostd/detail/dependent_type.h" -#include "opentelemetry/nostd/detail/find_index.h" -#include "opentelemetry/nostd/detail/functional.h" -#include "opentelemetry/nostd/detail/recursive_union.h" -#include "opentelemetry/nostd/detail/trait.h" -#include "opentelemetry/nostd/detail/type_pack_element.h" -#include "opentelemetry/nostd/detail/variant_alternative.h" -#include "opentelemetry/nostd/detail/variant_fwd.h" -#include "opentelemetry/nostd/detail/variant_size.h" -#include "opentelemetry/nostd/type_traits.h" -#include "opentelemetry/nostd/utility.h" -#include "opentelemetry/version.h" - -#define AUTO_RETURN(...) \ - ->decay_t { return __VA_ARGS__; } - -#define AUTO_REFREF_RETURN(...) \ - ->decltype((__VA_ARGS__)) \ - { \ - static_assert(std::is_reference::value, ""); \ - return __VA_ARGS__; \ - } - -#define DECLTYPE_AUTO_RETURN(...) \ - ->decltype(__VA_ARGS__) { return __VA_ARGS__; } - +#ifdef HAVE_CPP_STDLIB +# include "opentelemetry/std/variant.h" +#elif defined(HAVE_ABSEIL) +// TODO: prefer `absl::variant` over `nostd::variant` since the latter does not compile with vs2015 +# include "absl/types/variant.h" OPENTELEMETRY_BEGIN_NAMESPACE namespace nostd { -constexpr std::size_t variant_npos = static_cast(-1); - -class bad_variant_access : public std::exception -{ -public: - virtual const char *what() const noexcept override { return "bad_variant_access"; } -}; - -[[noreturn]] inline void throw_bad_variant_access() -{ -#if __EXCEPTIONS - throw bad_variant_access{}; -#else - std::terminate(); -#endif -} - -namespace detail -{ -namespace access -{ -struct base -{ - template - inline static constexpr auto get_alt(V &&v) -#ifdef _MSC_VER - AUTO_REFREF_RETURN(recursive_union::get_alt(std::forward(v).data_, in_place_index_t{})) -#else - AUTO_REFREF_RETURN(recursive_union::get_alt(data(std::forward(v)), in_place_index_t{})) -#endif -}; - -struct variant -{ - template - inline static constexpr auto get_alt(V &&v) - AUTO_REFREF_RETURN(base::get_alt(std::forward(v).impl_)) -}; -} // namespace access -} // namespace detail - -namespace detail -{ -namespace visitation -{ - -struct base -{ - template - using dispatch_result_t = decltype( - nostd::invoke(std::declval(), access::base::get_alt<0>(std::declval())...)); - - template - struct expected - { - template - inline static constexpr bool but_got() - { - return std::is_same::value; - } - }; - - template - struct visit_return_type_check - { - static_assert(expected::template but_got(), - "`visit` requires the visitor to have a single return type"); - - template - inline static constexpr auto invoke(Visitor &&visitor, Alts &&... alts) - DECLTYPE_AUTO_RETURN(nostd::invoke(std::forward(visitor), - std::forward(alts)...)) - }; - - template - inline static constexpr const T &at(const T &elem) noexcept - { - return elem; - } - - template - inline static constexpr const remove_all_extents_t &at(const std::array &elems, - std::size_t i, - Is... is) noexcept - { - return at(elems[i], is...); - } - - template - inline static constexpr std::array, sizeof...(Fs) + 1> make_farray(F &&f, Fs &&... fs) - { - return {{std::forward(f), std::forward(fs)...}}; - } - - template - struct make_fmatrix_impl - { - - template - inline static constexpr dispatch_result_t dispatch(F &&f, Vs &&... vs) - { - using Expected = dispatch_result_t; - using Actual = decltype( - nostd::invoke(std::forward(f), access::base::get_alt(std::forward(vs))...)); - return visit_return_type_check::invoke( - std::forward(f), access::base::get_alt(std::forward(vs))...); - } - - template - struct impl; - - template - struct impl> - { - inline constexpr auto operator()() const AUTO_RETURN(&dispatch) - }; - - template - struct impl, Ls...> - { - inline constexpr auto operator()() const - AUTO_RETURN(make_farray(impl, Ls...>{}()...)) - }; - }; - - template - inline static constexpr auto make_fmatrix() AUTO_RETURN( - typename make_fmatrix_impl:: - template impl, make_index_sequence::size()>...>{}()) - - template - struct make_fdiagonal_impl - { - template - inline static constexpr dispatch_result_t dispatch(F &&f, Vs &&... vs) - { - using Expected = dispatch_result_t; - using Actual = decltype( - nostd::invoke(std::forward(f), access::base::get_alt(std::forward(vs))...)); - return visit_return_type_check::invoke( - std::forward(f), access::base::get_alt(std::forward(vs))...); - } - - template - inline static constexpr auto impl(index_sequence) - AUTO_RETURN(make_farray(&dispatch...)) - }; - - template - inline static constexpr auto make_fdiagonal() - -> decltype(make_fdiagonal_impl::impl(make_index_sequence::size()>{})) - { - static_assert(all<(decay_t::size() == decay_t::size())...>::value, - "all of the variants must be the same size."); - return make_fdiagonal_impl::impl(make_index_sequence::size()>{}); - } -}; - -#if !defined(_MSC_VER) || _MSC_VER >= 1910 -template -using fmatrix_t = decltype(base::make_fmatrix()); - -template -struct fmatrix -{ - static constexpr fmatrix_t value = base::make_fmatrix(); -}; - -template -constexpr fmatrix_t fmatrix::value; - -template -using fdiagonal_t = decltype(base::make_fdiagonal()); - -template -struct fdiagonal -{ - static constexpr fdiagonal_t value = base::make_fdiagonal(); -}; - -template -constexpr fdiagonal_t fdiagonal::value; -#endif - -struct alt -{ - template - inline static constexpr auto visit_alt(Visitor &&visitor, Vs &&... vs) DECLTYPE_AUTO_RETURN( - base::at(base::make_fmatrix(vs)))...>(), - vs.index()...)(std::forward(visitor), as_base(std::forward(vs))...)) - - template - inline static constexpr auto visit_alt_at(std::size_t index, Visitor &&visitor, Vs &&... vs) - DECLTYPE_AUTO_RETURN(base::at( - base::make_fdiagonal(vs)))...>(), - index)(std::forward(visitor), as_base(std::forward(vs))...)) -}; - -struct variant -{ -private: - template - struct visitor - { - template - inline static constexpr bool does_not_handle() - { - return nostd::is_invocable::value; - } - }; - - template - struct visit_exhaustiveness_check - { - static_assert(visitor::template does_not_handle(), - "`visit` requires the visitor to be exhaustive."); - - inline static constexpr auto invoke(Visitor &&visitor, Values &&... values) - DECLTYPE_AUTO_RETURN(nostd::invoke(std::forward(visitor), - std::forward(values)...)) - }; - - template - struct value_visitor - { - Visitor &&visitor_; - - template - inline constexpr auto operator()(Alts &&... alts) const DECLTYPE_AUTO_RETURN( - visit_exhaustiveness_check(alts).value))...>::invoke( - std::forward(visitor_), - std::forward(alts).value...)) - }; - - template - inline static constexpr auto make_value_visitor(Visitor &&visitor) - AUTO_RETURN(value_visitor{std::forward(visitor)}) - - public - : template - inline static constexpr auto visit_alt(Visitor &&visitor, Vs &&... vs) - DECLTYPE_AUTO_RETURN(alt::visit_alt(std::forward(visitor), - std::forward(vs).impl_...)) - - template - inline static constexpr auto visit_alt_at(std::size_t index, - Visitor &&visitor, - Vs &&... vs) - DECLTYPE_AUTO_RETURN(alt::visit_alt_at(index, - std::forward(visitor), - std::forward(vs).impl_...)) - - template - inline static constexpr auto visit_value(Visitor &&visitor, Vs &&... vs) - DECLTYPE_AUTO_RETURN( - visit_alt(make_value_visitor(std::forward(visitor)), - std::forward(vs)...)) - - template - inline static constexpr auto visit_value_at(std::size_t index, - Visitor &&visitor, - Vs &&... vs) - DECLTYPE_AUTO_RETURN(visit_alt_at( - index, - make_value_visitor(std::forward(visitor)), - std::forward(vs)...)) -}; -} // namespace visitation - -template -using index_t = typename std::conditional< - sizeof...(Ts) < (std::numeric_limits::max)(), - unsigned char, - typename std::conditional::max)(), - unsigned short, - unsigned int>::type>::type; - -template -class base -{ -public: - inline explicit constexpr base(valueless_t tag) noexcept - : data_(tag), index_(static_cast>(-1)) - {} - - template - inline explicit constexpr base(in_place_index_t, Args &&... args) - : data_(in_place_index_t{}, std::forward(args)...), index_(I) - {} - - inline constexpr bool valueless_by_exception() const noexcept - { - return index_ == static_cast>(-1); - } - - inline constexpr std::size_t index() const noexcept - { - return valueless_by_exception() ? variant_npos : index_; - } - -protected: - using data_t = recursive_union; - - friend inline constexpr base &as_base(base &b) { return b; } - friend inline constexpr const base &as_base(const base &b) { return b; } - friend inline constexpr base &&as_base(base &&b) { return std::move(b); } - friend inline constexpr const base &&as_base(const base &&b) { return std::move(b); } - - friend inline constexpr data_t &data(base &b) { return b.data_; } - friend inline constexpr const data_t &data(const base &b) { return b.data_; } - friend inline constexpr data_t &&data(base &&b) { return std::move(b).data_; } - friend inline constexpr const data_t &&data(const base &&b) { return std::move(b).data_; } - - inline static constexpr std::size_t size() { return sizeof...(Ts); } - - data_t data_; - index_t index_; - - friend struct access::base; - friend struct visitation::base; -}; - -struct dtor -{ -#ifdef _MSC_VER -# pragma warning(push) -# pragma warning(disable : 4100) -#endif - template - inline void operator()(Alt &alt) const noexcept - { - alt.~Alt(); - } -#ifdef _MSC_VER -# pragma warning(pop) -#endif -}; - -template -class destructor; - -#define OPENTELEMETRY_VARIANT_DESTRUCTOR(destructible_trait, definition, destroy) \ - template \ - class destructor, destructible_trait> : public base \ - { \ - using super = base; \ - \ - public: \ - using super::super; \ - using super::operator=; \ - \ - destructor(const destructor &) = default; \ - destructor(destructor &&) = default; \ - definition destructor &operator=(const destructor &) = default; \ - destructor &operator=(destructor &&) = default; \ - \ - protected: \ - destroy \ - } - -OPENTELEMETRY_VARIANT_DESTRUCTOR( - Trait::TriviallyAvailable, ~destructor() = default; - , inline void destroy() noexcept { this->index_ = static_cast>(-1); }); - -OPENTELEMETRY_VARIANT_DESTRUCTOR( - Trait::Available, - ~destructor() { destroy(); }, - inline void destroy() noexcept { - if (!this->valueless_by_exception()) - { - visitation::alt::visit_alt(dtor{}, *this); - } - this->index_ = static_cast>(-1); - }); - -OPENTELEMETRY_VARIANT_DESTRUCTOR(Trait::Unavailable, ~destructor() = delete; - , inline void destroy() noexcept = delete;); - -#undef OPENTELEMETRY_VARIANT_DESTRUCTOR - -template -class constructor : public destructor -{ - using super = destructor; - -public: - using super::super; - using super::operator=; - -protected: - struct ctor - { - template - inline void operator()(LhsAlt &lhs_alt, RhsAlt &&rhs_alt) const - { - constructor::construct_alt(lhs_alt, std::forward(rhs_alt).value); - } - }; - - template - inline static T &construct_alt(alt &a, Args &&... args) - { - auto *result = ::new (static_cast(std::addressof(a))) - alt(in_place_t{}, std::forward(args)...); - return result->value; - } - - template - inline static void generic_construct(constructor &lhs, Rhs &&rhs) - { - lhs.destroy(); - if (!rhs.valueless_by_exception()) - { - visitation::alt::visit_alt_at(rhs.index(), ctor{}, lhs, std::forward(rhs)); - lhs.index_ = rhs.index_; - } - } -}; - -template -class move_constructor; - -#define OPENTELEMETRY_VARIANT_MOVE_CONSTRUCTOR(move_constructible_trait, definition) \ - template \ - class move_constructor, move_constructible_trait> \ - : public constructor> \ - { \ - using super = constructor>; \ - \ - public: \ - using super::super; \ - using super::operator=; \ - \ - move_constructor(const move_constructor &) = default; \ - definition ~move_constructor() = default; \ - move_constructor &operator=(const move_constructor &) = default; \ - move_constructor &operator=(move_constructor &&) = default; \ - } - -OPENTELEMETRY_VARIANT_MOVE_CONSTRUCTOR(Trait::TriviallyAvailable, - move_constructor(move_constructor &&that) = default;); - -OPENTELEMETRY_VARIANT_MOVE_CONSTRUCTOR( - Trait::Available, - move_constructor(move_constructor &&that) noexcept( - all::value...>::value) - : move_constructor(valueless_t{}) { this->generic_construct(*this, std::move(that)); }); - -OPENTELEMETRY_VARIANT_MOVE_CONSTRUCTOR(Trait::Unavailable, - move_constructor(move_constructor &&) = delete;); - -#undef OPENTELEMETRY_VARIANT_MOVE_CONSTRUCTOR - -template -class copy_constructor; - -#define OPENTELEMETRY_VARIANT_COPY_CONSTRUCTOR(copy_constructible_trait, definition) \ - template \ - class copy_constructor, copy_constructible_trait> \ - : public move_constructor> \ - { \ - using super = move_constructor>; \ - \ - public: \ - using super::super; \ - using super::operator=; \ - \ - definition copy_constructor(copy_constructor &&) = default; \ - ~copy_constructor() = default; \ - copy_constructor &operator=(const copy_constructor &) = default; \ - copy_constructor &operator=(copy_constructor &&) = default; \ - } - -OPENTELEMETRY_VARIANT_COPY_CONSTRUCTOR(Trait::TriviallyAvailable, - copy_constructor(const copy_constructor &that) = default;); - -OPENTELEMETRY_VARIANT_COPY_CONSTRUCTOR( - Trait::Available, copy_constructor(const copy_constructor &that) - : copy_constructor(valueless_t{}) { this->generic_construct(*this, that); }); - -OPENTELEMETRY_VARIANT_COPY_CONSTRUCTOR(Trait::Unavailable, - copy_constructor(const copy_constructor &) = delete;); - -#undef OPENTELEMETRY_VARIANT_COPY_CONSTRUCTOR - -template -class assignment : public copy_constructor -{ - using super = copy_constructor; - -public: - using super::super; - using super::operator=; - - template - inline /* auto & */ auto emplace(Args &&... args) - -> decltype(this->construct_alt(access::base::get_alt(*this), std::forward(args)...)) - { - this->destroy(); - auto &result = - this->construct_alt(access::base::get_alt(*this), std::forward(args)...); - this->index_ = I; - return result; - } - -protected: - template - struct assigner - { - template - inline void operator()(ThisAlt &this_alt, ThatAlt &&that_alt) const - { - self->assign_alt(this_alt, std::forward(that_alt).value); - } - assignment *self; - }; - - template - inline void assign_alt(alt &a, Arg &&arg) - { - if (this->index() == I) - { -#ifdef _MSC_VER -# pragma warning(push) -# pragma warning(disable : 4244) -#endif - a.value = std::forward(arg); -#ifdef _MSC_VER -# pragma warning(pop) -#endif - } - else - { - struct - { - void operator()(std::true_type) const { this_->emplace(std::forward(arg_)); } - void operator()(std::false_type) const { this_->emplace(T(std::forward(arg_))); } - assignment *this_; - Arg &&arg_; - } impl{this, std::forward(arg)}; - impl(bool_constant < std::is_nothrow_constructible::value || - !std::is_nothrow_move_constructible::value > {}); - } - } - - template - inline void generic_assign(That &&that) - { - if (this->valueless_by_exception() && that.valueless_by_exception()) - { - // do nothing. - } - else if (that.valueless_by_exception()) - { - this->destroy(); - } - else - { - visitation::alt::visit_alt_at(that.index(), assigner{this}, *this, - std::forward(that)); - } - } -}; - -template -class move_assignment; - -#define OPENTELEMETRY_VARIANT_MOVE_ASSIGNMENT(move_assignable_trait, definition) \ - template \ - class move_assignment, move_assignable_trait> : public assignment> \ - { \ - using super = assignment>; \ - \ - public: \ - using super::super; \ - using super::operator=; \ - \ - move_assignment(const move_assignment &) = default; \ - move_assignment(move_assignment &&) = default; \ - ~move_assignment() = default; \ - move_assignment &operator=(const move_assignment &) = default; \ - definition \ - } - -OPENTELEMETRY_VARIANT_MOVE_ASSIGNMENT( - Trait::TriviallyAvailable, move_assignment &operator=(move_assignment &&that) = default;); - -OPENTELEMETRY_VARIANT_MOVE_ASSIGNMENT( - Trait::Available, - move_assignment & - operator=(move_assignment &&that) noexcept( - all<(std::is_nothrow_move_constructible::value && - std::is_nothrow_move_assignable::value)...>::value) { - this->generic_assign(std::move(that)); - return *this; - }); - -OPENTELEMETRY_VARIANT_MOVE_ASSIGNMENT(Trait::Unavailable, - move_assignment &operator=(move_assignment &&) = delete;); - -#undef OPENTELEMETRY_VARIANT_MOVE_ASSIGNMENT - -template -class copy_assignment; - -#define OPENTELEMETRY_VARIANT_COPY_ASSIGNMENT(copy_assignable_trait, definition) \ - template \ - class copy_assignment, copy_assignable_trait> \ - : public move_assignment> \ - { \ - using super = move_assignment>; \ - \ - public: \ - using super::super; \ - using super::operator=; \ - \ - copy_assignment(const copy_assignment &) = default; \ - copy_assignment(copy_assignment &&) = default; \ - ~copy_assignment() = default; \ - definition copy_assignment &operator=(copy_assignment &&) = default; \ - } - -OPENTELEMETRY_VARIANT_COPY_ASSIGNMENT( - Trait::TriviallyAvailable, copy_assignment &operator=(const copy_assignment &that) = default;); - -OPENTELEMETRY_VARIANT_COPY_ASSIGNMENT( - Trait::Available, - copy_assignment & - operator=(const copy_assignment &that) { - this->generic_assign(that); - return *this; - }); - -OPENTELEMETRY_VARIANT_COPY_ASSIGNMENT( - Trait::Unavailable, copy_assignment &operator=(const copy_assignment &) = delete;); - -#undef OPENTELEMETRY_VARIANT_COPY_ASSIGNMENT -template -class impl : public copy_assignment> -{ - using super = copy_assignment>; - -public: - using super::super; - using super::operator=; - - impl(const impl &) = default; - impl(impl &&) = default; - ~impl() = default; - impl &operator=(const impl &) = default; - impl &operator=(impl &&) = default; - - template - inline void assign(Arg &&arg) - { - this->assign_alt(access::base::get_alt(*this), std::forward(arg)); - } - - inline void swap(impl &that) - { - if (this->valueless_by_exception() && that.valueless_by_exception()) - { - // do nothing. - } - else if (this->index() == that.index()) - { - visitation::alt::visit_alt_at(this->index(), swapper{}, *this, that); - } - else - { - impl *lhs = this; - impl *rhs = std::addressof(that); - if (lhs->move_nothrow() && !rhs->move_nothrow()) - { - std::swap(lhs, rhs); - } - impl tmp(std::move(*rhs)); -#if __EXCEPTIONS - // EXTENSION: When the move construction of `lhs` into `rhs` throws - // and `tmp` is nothrow move constructible then we move `tmp` back - // into `rhs` and provide the strong exception safety guarantee. - try - { - this->generic_construct(*rhs, std::move(*lhs)); - } - catch (...) - { - if (tmp.move_nothrow()) - { - this->generic_construct(*rhs, std::move(tmp)); - } - throw; - } -#else - this->generic_construct(*rhs, std::move(*lhs)); -#endif - this->generic_construct(*lhs, std::move(tmp)); - } - } - -private: - struct swapper - { - template - inline void operator()(ThisAlt &this_alt, ThatAlt &that_alt) const - { - using std::swap; - swap(this_alt.value, that_alt.value); - } - }; - - inline constexpr bool move_nothrow() const - { - return this->valueless_by_exception() || - std::array{ - {std::is_nothrow_move_constructible::value...}}[this->index()]; - } -}; - -template -struct is_non_narrowing_convertible -{ - template - static std::true_type test(T(&&)[1]); - - template - static auto impl(int) -> decltype(test({std::declval()})); - - template - static auto impl(...) -> std::false_type; - - static constexpr bool value = decltype(impl(0))::value; -}; - -template ::value, - typename = void> -struct overload_leaf -{}; - -template -struct overload_leaf -{ - using impl = size_constant (*)(T); - operator impl() const { return nullptr; }; -}; - -template -struct overload_leaf, bool>::value - ? std::is_same, bool>::value - : -#if defined(__clang__) || !defined(__GNUC__) || __GNUC__ >= 5 - is_non_narrowing_convertible::value -#else - std::is_convertible::value -#endif - >> -{ - using impl = size_constant (*)(T); - operator impl() const { return nullptr; }; -}; - -template -struct overload_impl -{ -private: - template - struct impl; - - template - struct impl> : overload_leaf... - {}; - -public: - using type = impl>; -}; - -template -using overload = typename overload_impl::type; - -template -using best_match = invoke_result_t, Arg>; - -template -struct is_in_place_index : std::false_type -{}; - -template -struct is_in_place_index> : std::true_type -{}; - -template -struct is_in_place_type : std::false_type -{}; - -template -struct is_in_place_type> : std::true_type -{}; -} // namespace detail - -template -class variant -{ - static_assert(0 < sizeof...(Ts), "variant must consist of at least one alternative."); - - static_assert(detail::all::value...>::value, - "variant can not have an array type as an alternative."); - - static_assert(detail::all::value...>::value, - "variant can not have a reference type as an alternative."); - - static_assert(detail::all::value...>::value, - "variant can not have a void type as an alternative."); - -public: - template , - enable_if_t::value, int> = 0> - inline constexpr variant() noexcept(std::is_nothrow_default_constructible::value) - : impl_(in_place_index_t<0>{}) - {} - - variant(const variant &) = default; - variant(variant &&) = default; - - template , - enable_if_t::value, int> = 0, - enable_if_t::value, int> = 0, - enable_if_t::value, int> = 0, - std::size_t I = detail::best_match::value, - typename T = detail::type_pack_element_t, - enable_if_t::value, int> = 0> - inline constexpr variant(Arg &&arg) noexcept(std::is_nothrow_constructible::value) - : impl_(in_place_index_t{}, std::forward(arg)) - {} - - template , - enable_if_t::value, int> = 0> - inline explicit constexpr variant(in_place_index_t, Args &&... args) noexcept( - std::is_nothrow_constructible::value) - : impl_(in_place_index_t{}, std::forward(args)...) - {} - - template < - std::size_t I, - typename Up, - typename... Args, - typename T = detail::type_pack_element_t, - enable_if_t &, Args...>::value, int> = 0> - inline explicit constexpr variant( - in_place_index_t, - std::initializer_list il, - Args &&... args) noexcept(std::is_nothrow_constructible &, - Args...>::value) - : impl_(in_place_index_t{}, il, std::forward(args)...) - {} - - template ::value, - enable_if_t::value, int> = 0> - inline explicit constexpr variant(in_place_type_t, Args &&... args) noexcept( - std::is_nothrow_constructible::value) - : impl_(in_place_index_t{}, std::forward(args)...) - {} - - template < - typename T, - typename Up, - typename... Args, - std::size_t I = detail::find_index_sfinae::value, - enable_if_t &, Args...>::value, int> = 0> - inline explicit constexpr variant( - in_place_type_t, - std::initializer_list il, - Args &&... args) noexcept(std::is_nothrow_constructible &, - Args...>::value) - : impl_(in_place_index_t{}, il, std::forward(args)...) - {} - - ~variant() = default; - - variant &operator=(const variant &) = default; - variant &operator=(variant &&) = default; - - template < - typename Arg, - enable_if_t, variant>::value, int> = 0, - std::size_t I = detail::best_match::value, - typename T = detail::type_pack_element_t, - enable_if_t<(std::is_assignable::value && std::is_constructible::value), - int> = 0> - inline variant &operator=(Arg &&arg) noexcept((std::is_nothrow_assignable::value && - std::is_nothrow_constructible::value)) - { - impl_.template assign(std::forward(arg)); - return *this; - } - - template , - enable_if_t::value, int> = 0> - inline T &emplace(Args &&... args) - { - return impl_.template emplace(std::forward(args)...); - } - - template < - std::size_t I, - typename Up, - typename... Args, - typename T = detail::type_pack_element_t, - enable_if_t &, Args...>::value, int> = 0> - inline T &emplace(std::initializer_list il, Args &&... args) - { - return impl_.template emplace(il, std::forward(args)...); - } - - template ::value, - enable_if_t::value, int> = 0> - inline T &emplace(Args &&... args) - { - return impl_.template emplace(std::forward(args)...); - } - - template < - typename T, - typename Up, - typename... Args, - std::size_t I = detail::find_index_sfinae::value, - enable_if_t &, Args...>::value, int> = 0> - inline T &emplace(std::initializer_list il, Args &&... args) - { - return impl_.template emplace(il, std::forward(args)...); - } - - inline constexpr bool valueless_by_exception() const noexcept - { - return impl_.valueless_by_exception(); - } - - inline constexpr std::size_t index() const noexcept { return impl_.index(); } - - template , Dummy>::value && - detail::dependent_type, Dummy>::value)...>::value, - int> = 0> - inline void swap(variant &that) noexcept( - detail::all<(std::is_nothrow_move_constructible::value && - is_nothrow_swappable::value)...>::value) - { - impl_.swap(that.impl_); - } - -private: - detail::impl impl_; - - friend struct detail::access::variant; - friend struct detail::visitation::variant; -}; - -template -inline constexpr bool holds_alternative(const variant &v) noexcept -{ - return v.index() == I; -} - -template -inline constexpr bool holds_alternative(const variant &v) noexcept -{ - return holds_alternative::value>(v); -} - -namespace detail -{ -template -struct generic_get_impl -{ - constexpr generic_get_impl(int) noexcept {} - - constexpr auto operator()(V &&v) const - AUTO_REFREF_RETURN(access::variant::get_alt(std::forward(v)).value) -}; - -template -inline constexpr auto generic_get(V &&v) AUTO_REFREF_RETURN(generic_get_impl( - holds_alternative(v) ? 0 : (throw_bad_variant_access(), 0))(std::forward(v))) -} // namespace detail - -template -inline constexpr variant_alternative_t> &get(variant &v) -{ - return detail::generic_get(v); -} - -template -inline constexpr variant_alternative_t> &&get(variant &&v) -{ - return detail::generic_get(std::move(v)); -} - -template -inline constexpr const variant_alternative_t> &get(const variant &v) -{ - return detail::generic_get(v); -} - -template -inline constexpr const variant_alternative_t> &&get(const variant &&v) -{ - return detail::generic_get(std::move(v)); -} - -template -inline constexpr T &get(variant &v) -{ - return get::value>(v); -} - -template -inline constexpr T &&get(variant &&v) -{ - return get::value>(std::move(v)); -} - -template -inline constexpr const T &get(const variant &v) -{ - return get::value>(v); -} - -template -inline constexpr const T &&get(const variant &&v) -{ - return get::value>(std::move(v)); -} - -namespace detail -{ - -template -inline constexpr /* auto * */ auto generic_get_if(V *v) noexcept AUTO_RETURN( - v &&holds_alternative(*v) ? std::addressof(access::variant::get_alt(*v).value) : nullptr) - -} // namespace detail - -template -inline constexpr add_pointer_t>> get_if( - variant *v) noexcept -{ - return detail::generic_get_if(v); -} - -template -inline constexpr add_pointer_t>> get_if( - const variant *v) noexcept -{ - return detail::generic_get_if(v); -} - -template -inline constexpr add_pointer_t get_if(variant *v) noexcept -{ - return get_if::value>(v); -} - -template -inline constexpr add_pointer_t get_if(const variant *v) noexcept -{ - return get_if::value>(v); -} - -namespace detail -{ -template -struct convert_to_bool -{ - template - inline constexpr bool operator()(Lhs &&lhs, Rhs &&rhs) const - { - static_assert(std::is_convertible, bool>::value, - "relational operators must return a type" - " implicitly convertible to bool"); - return nostd::invoke(RelOp{}, std::forward(lhs), std::forward(rhs)); - } -}; -} // namespace detail - -template -inline constexpr bool operator==(const variant &lhs, const variant &rhs) -{ - using detail::visitation::variant; - using equal_to = detail::convert_to_bool; - return lhs.index() == rhs.index() && (lhs.valueless_by_exception() || - variant::visit_value_at(lhs.index(), equal_to{}, lhs, rhs)); -} - -template -inline constexpr bool operator!=(const variant &lhs, const variant &rhs) -{ - using detail::visitation::variant; - using not_equal_to = detail::convert_to_bool; - return lhs.index() != rhs.index() || - (!lhs.valueless_by_exception() && - variant::visit_value_at(lhs.index(), not_equal_to{}, lhs, rhs)); -} - -template -inline constexpr bool operator<(const variant &lhs, const variant &rhs) -{ - using detail::visitation::variant; - using less = detail::convert_to_bool; - return !rhs.valueless_by_exception() && - (lhs.valueless_by_exception() || lhs.index() < rhs.index() || - (lhs.index() == rhs.index() && variant::visit_value_at(lhs.index(), less{}, lhs, rhs))); -} - -template -inline constexpr bool operator>(const variant &lhs, const variant &rhs) -{ - using detail::visitation::variant; - using greater = detail::convert_to_bool; - return !lhs.valueless_by_exception() && - (rhs.valueless_by_exception() || lhs.index() > rhs.index() || - (lhs.index() == rhs.index() && - variant::visit_value_at(lhs.index(), greater{}, lhs, rhs))); -} - -template -inline constexpr bool operator<=(const variant &lhs, const variant &rhs) -{ - using detail::visitation::variant; - using less_equal = detail::convert_to_bool; - return lhs.valueless_by_exception() || - (!rhs.valueless_by_exception() && - (lhs.index() < rhs.index() || - (lhs.index() == rhs.index() && - variant::visit_value_at(lhs.index(), less_equal{}, lhs, rhs)))); -} - -template -inline constexpr bool operator>=(const variant &lhs, const variant &rhs) -{ - using detail::visitation::variant; - using greater_equal = detail::convert_to_bool; - return rhs.valueless_by_exception() || - (!lhs.valueless_by_exception() && - (lhs.index() > rhs.index() || - (lhs.index() == rhs.index() && - variant::visit_value_at(lhs.index(), greater_equal{}, lhs, rhs)))); -} - -struct monostate -{}; - -inline constexpr bool operator<(monostate, monostate) noexcept -{ - return false; -} - -inline constexpr bool operator>(monostate, monostate) noexcept -{ - return false; -} - -inline constexpr bool operator<=(monostate, monostate) noexcept -{ - return true; -} - -inline constexpr bool operator>=(monostate, monostate) noexcept -{ - return true; -} - -inline constexpr bool operator==(monostate, monostate) noexcept -{ - return true; -} - -inline constexpr bool operator!=(monostate, monostate) noexcept -{ - return false; -} - -namespace detail -{ - -template -inline constexpr bool all_of_impl(const std::array &bs, std::size_t idx) -{ - return idx >= N || (bs[idx] && all_of_impl(bs, idx + 1)); -} - -template -inline constexpr bool all_of(const std::array &bs) -{ - return all_of_impl(bs, 0); -} - -} // namespace detail - -template -inline constexpr auto visit(Visitor &&visitor, Vs &&... vs) DECLTYPE_AUTO_RETURN( - (detail::all_of(std::array{{!vs.valueless_by_exception()...}}) - ? (void)0 - : throw_bad_variant_access()), - detail::visitation::variant::visit_value(std::forward(visitor), - std::forward(vs)...)) template -inline auto swap(variant &lhs, variant &rhs) noexcept(noexcept(lhs.swap(rhs))) - -> decltype(lhs.swap(rhs)) -{ - lhs.swap(rhs); -} +using absl::get; +using absl::holds_alternative; +using absl::variant; +using absl::visit; } // namespace nostd OPENTELEMETRY_END_NAMESPACE - -#undef AUTO_RETURN - -#undef AUTO_REFREF_RETURN - -#undef DECLTYPE_AUTO_RETURN +#else +# include "opentelemetry/nostd/mpark/variant.h" +#endif diff --git a/api/include/opentelemetry/std/shared_ptr.h b/api/include/opentelemetry/std/shared_ptr.h new file mode 100644 index 0000000000..8969f2ced5 --- /dev/null +++ b/api/include/opentelemetry/std/shared_ptr.h @@ -0,0 +1,31 @@ +// Copyright 2020, OpenTelemetry 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 +// +// http://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. + +#pragma once + +#include "opentelemetry/version.h" + +#include + +OPENTELEMETRY_BEGIN_NAMESPACE +// Standard Type aliases in nostd namespace +namespace nostd +{ + +// nostd::shared_ptr +template +using shared_ptr = std::shared_ptr<_Types...>; + +} // namespace nostd +OPENTELEMETRY_END_NAMESPACE diff --git a/api/include/opentelemetry/std/span.h b/api/include/opentelemetry/std/span.h new file mode 100644 index 0000000000..da9fcb48a3 --- /dev/null +++ b/api/include/opentelemetry/std/span.h @@ -0,0 +1,76 @@ +// Copyright 2020, OpenTelemetry 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 +// +// http://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. + +#pragma once + +#include "opentelemetry/version.h" + +// Standard library implementation requires at least C++17 compiler. +// Older C++14 compilers may provide support for __has_include as a +// conforming extension. +#if defined __has_include +# if __has_include() // Check for __cpp_{feature} +# include +# if defined(__cpp_lib_span) +# define HAVE_SPAN +# endif +# endif +# if __has_include() && !defined(HAVE_SPAN) // Check for span +# define HAVE_SPAN +# endif +# if !__has_include() // Check for string_view +# error \ + "STL library does not support std::span. Possible solution:" \ + " - #undef HAVE_CPP_STDLIB // to use OpenTelemetry nostd::string_view" +# endif +#endif + +#if !defined(HAVE_SPAN) +# if defined(HAVE_GSL) +# include +// Guidelines Support Library provides an implementation of std::span +# include +OPENTELEMETRY_BEGIN_NAMESPACE +namespace nostd +{ +template +using span = gsl::span; +} +OPENTELEMETRY_END_NAMESPACE +# else +// No span implementation provided. +# error \ + "STL library does not support std::span. Possible solutions:" \ + " - #undef HAVE_CPP_STDLIB // to use OpenTelemetry nostd::span .. or " \ + " - #define HAVE_GSL // to use gsl::span " +# endif + +#else // HAVE_SPAN +// Using std::span (https://wg21.link/P0122R7) from Standard Library available in C++20 : +// - GCC libstdc++ 10+ +// - Clang libc++ 7 +// - MSVC Standard Library 19.26* +// - Apple Clang 10.0.0* +# include +# include +OPENTELEMETRY_BEGIN_NAMESPACE +namespace nostd +{ +constexpr std::size_t dynamic_extent = std::numeric_limits::max(); + +template +using span = std::span; +} // namespace nostd +OPENTELEMETRY_END_NAMESPACE +#endif // of HAVE_SPAN diff --git a/api/include/opentelemetry/std/string_view.h b/api/include/opentelemetry/std/string_view.h new file mode 100644 index 0000000000..755b4387a1 --- /dev/null +++ b/api/include/opentelemetry/std/string_view.h @@ -0,0 +1,36 @@ +// Copyright 2020, OpenTelemetry 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 +// +// http://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. + +#pragma once + +#include "opentelemetry/version.h" + +#include "opentelemetry/std/utility.h" + +#include +#include +#include +#include +#include + +OPENTELEMETRY_BEGIN_NAMESPACE +// Standard Type aliases in nostd namespace +namespace nostd +{ + +// nostd::string_view +using string_view = std::string_view; + +} // namespace nostd +OPENTELEMETRY_END_NAMESPACE diff --git a/api/include/opentelemetry/std/type_traits.h b/api/include/opentelemetry/std/type_traits.h new file mode 100644 index 0000000000..1fd6ef7f88 --- /dev/null +++ b/api/include/opentelemetry/std/type_traits.h @@ -0,0 +1,31 @@ +// Copyright 2020, OpenTelemetry 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 +// +// http://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. + +#pragma once + +#include "opentelemetry/version.h" + +#include + +OPENTELEMETRY_BEGIN_NAMESPACE +// Standard Type aliases in nostd namespace +namespace nostd +{ + +// nostd::enable_if_t<...> +template +using enable_if_t = typename std::enable_if::type; + +} // namespace nostd +OPENTELEMETRY_END_NAMESPACE diff --git a/api/include/opentelemetry/std/unique_ptr.h b/api/include/opentelemetry/std/unique_ptr.h new file mode 100644 index 0000000000..7877d2d854 --- /dev/null +++ b/api/include/opentelemetry/std/unique_ptr.h @@ -0,0 +1,31 @@ +// Copyright 2020, OpenTelemetry 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 +// +// http://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. + +#pragma once + +#include "opentelemetry/version.h" + +#include + +OPENTELEMETRY_BEGIN_NAMESPACE +// Standard Type aliases in nostd namespace +namespace nostd +{ + +// nostd::unique_ptr +template +using unique_ptr = std::unique_ptr<_Types...>; + +} // namespace nostd +OPENTELEMETRY_END_NAMESPACE diff --git a/api/include/opentelemetry/std/utility.h b/api/include/opentelemetry/std/utility.h new file mode 100644 index 0000000000..a0d73c9fe9 --- /dev/null +++ b/api/include/opentelemetry/std/utility.h @@ -0,0 +1,80 @@ +// Copyright 2020, OpenTelemetry 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 +// +// http://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. + +#pragma once + +#include "opentelemetry/version.h" + +#include +#include + +OPENTELEMETRY_BEGIN_NAMESPACE +// Standard Type aliases in nostd namespace +namespace nostd +{ + +// +// Backport of std::data +// +// See https://en.cppreference.com/w/cpp/iterator/data +// +template +auto data(C &c) noexcept(noexcept(c.data())) -> decltype(c.data()) +{ + return c.data(); +} + +template +auto data(const C &c) noexcept(noexcept(c.data())) -> decltype(c.data()) +{ + return c.data(); +} + +template +T *data(T (&array)[N]) noexcept +{ + return array; +} + +template +const E *data(std::initializer_list list) noexcept +{ + return list.begin(); +} + +// +// Backport of std::size +// +// See https://en.cppreference.com/w/cpp/iterator/size +// +template +auto size(const C &c) noexcept(noexcept(c.size())) -> decltype(c.size()) +{ + return c.size(); +} + +template +std::size_t size(T (&array)[N]) noexcept +{ + return N; +} + +template +using make_index_sequence = std::make_index_sequence; + +template +using index_sequence = std::index_sequence; + +} // namespace nostd +OPENTELEMETRY_END_NAMESPACE diff --git a/api/include/opentelemetry/std/variant.h b/api/include/opentelemetry/std/variant.h new file mode 100644 index 0000000000..5d19ff325c --- /dev/null +++ b/api/include/opentelemetry/std/variant.h @@ -0,0 +1,235 @@ +// Copyright 2020, OpenTelemetry 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 +// +// http://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. + +#pragma once + +#include "opentelemetry/version.h" + +#include +#include +#include +#include + +OPENTELEMETRY_BEGIN_NAMESPACE +// Standard Type aliases in nostd namespace +namespace nostd +{ + +// nostd::variant<...> +template +using variant = std::variant<_Types...>; + +template +using variant_size = std::variant_size<_Types...>; + +#if defined(__APPLE__) && defined(_LIBCPP_USE_AVAILABILITY_APPLE) +// Apple Platforms provide std::bad_variant_access only in newer versions of OS. +// To keep API compatible with any version of OS - we are providing our own +// implementation of nostd::bad_variant_access exception. +# if __EXCEPTIONS + +// nostd::bad_variant_access +class bad_variant_access : public std::exception +{ +public: + virtual const char *what() const noexcept override { return "bad_variant_access"; } +}; + +[[noreturn]] inline void throw_bad_variant_access() +{ + throw bad_variant_access{}; +} +# endif + +# if __EXCEPTIONS +# define THROW_BAD_VARIANT_ACCESS throw_bad_variant_access() +# else +# define THROW_BAD_VARIANT_ACCESS std::terminate() +# endif + +// +// nostd::get<...> for Apple Clang +// +template +constexpr auto get_type = [](auto &&t) constexpr -> decltype(auto) +{ + auto v = t; + auto result = std::get_if(&v); // TODO: optimize with std::forward(t) if t is not rvalue + if (result) + { + return *result; + } + THROW_BAD_VARIANT_ACCESS; + return *result; +}; + +template +constexpr auto get_index = [](auto &&t) constexpr -> decltype(auto) +{ + auto v = t; + auto result = std::get_if(&v); // TODO: optimize with std::forward(t) if t is not rvalue + if (result) + { + return *result; + } + THROW_BAD_VARIANT_ACCESS; + return *result; +}; + +template +constexpr std::variant_alternative_t> &get(std::variant &v) +{ + return get_index(v); +}; + +template +constexpr std::variant_alternative_t> &&get(std::variant &&v) +{ + return get_index(std::forward(v)); +}; + +template +constexpr const std::variant_alternative_t> &get( + const std::variant &v) +{ + return get_index(v); +}; + +template +constexpr const std::variant_alternative_t> &&get( + const std::variant &&v) +{ + return get_index(std::forward(v)); +}; + +template +constexpr T &get(std::variant &v) +{ + return get_type(v); +}; + +template +constexpr T /*&&*/ get(std::variant &&v) +{ + return get_type(v); +}; + +template +constexpr const T &get(const std::variant &v) +{ + return get_type(v); +}; + +template +constexpr const T &&get(const std::variant &&v) +{ + return get_type(std::forward(v)); +}; + +template +constexpr auto visit(_Callable &&_Obj, _Variants &&... _Args) +{ + // Ref: + // https://stackoverflow.com/questions/52310835/xcode-10-call-to-unavailable-function-stdvisit + return std::__variant_detail::__visitation::__variant::__visit_value(_Obj, _Args...); +}; + +#else + +template +constexpr std::variant_alternative_t> &get(std::variant &v) +{ + return std::get(v); +}; + +template +constexpr std::variant_alternative_t> &&get(std::variant &&v) +{ + return std::get(std::forward(v)); +}; + +template +constexpr const std::variant_alternative_t> &get( + const std::variant &v) +{ + return std::get(v); +}; + +template +constexpr const std::variant_alternative_t> &&get( + const std::variant &&v) +{ + return std::get(std::forward(v)); +}; + +template +constexpr T &get(std::variant &v) +{ + return std::get(v); +}; + +template +constexpr T &&get(std::variant &&v) +{ + return std::get(std::forward(v)); +}; + +template +constexpr const T &get(const std::variant &v) +{ + return std::get(v); +}; + +template +constexpr const T &&get(const std::variant &&v) +{ + return std::get(std::forward(v)); +}; + +template +constexpr auto visit(_Callable &&_Obj, _Variants &&... _Args) +{ + return std::visit<_Callable, _Variants...>(static_cast<_Callable &&>(_Obj), + static_cast<_Variants &&>(_Args)...); +}; + +#endif + +/* +# if _HAS_CXX20 +template +constexpr _Ret visit(_Callable &&_Obj, _Variants &&... _Args) +{ + return std::visit<_Ret, _Callable, _Variants...>( + static_cast<_Callable &&>(_Obj), + static_cast<_Variants &&>(_Args)...); +}; +# endif +*/ + +// nostd::holds_alternative +template +inline constexpr bool holds_alternative(const variant &v) noexcept +{ + return v.index() == I; +} + +template +inline constexpr bool holds_alternative(const variant &v) noexcept +{ + return std::holds_alternative(v); +} + +} // namespace nostd +OPENTELEMETRY_END_NAMESPACE diff --git a/api/include/opentelemetry/trace/span.h b/api/include/opentelemetry/trace/span.h index f324658740..7235e96ae1 100644 --- a/api/include/opentelemetry/trace/span.h +++ b/api/include/opentelemetry/trace/span.h @@ -5,8 +5,10 @@ #include "opentelemetry/common/attribute_value.h" #include "opentelemetry/common/key_value_iterable_view.h" #include "opentelemetry/core/timestamp.h" +#include "opentelemetry/nostd/shared_ptr.h" #include "opentelemetry/nostd/span.h" #include "opentelemetry/nostd/string_view.h" +#include "opentelemetry/nostd/type_traits.h" #include "opentelemetry/nostd/unique_ptr.h" #include "opentelemetry/trace/canonical_code.h" #include "opentelemetry/trace/span_context.h" @@ -167,5 +169,14 @@ class Span // AddEvent). virtual bool IsRecording() const noexcept = 0; }; + +template +nostd::shared_ptr to_span_ptr(TracerType *objPtr, + nostd::string_view name, + const trace::StartSpanOptions &options) +{ + return nostd::shared_ptr{new (std::nothrow) SpanType{*objPtr, name, options}}; +} + } // namespace trace OPENTELEMETRY_END_NAMESPACE diff --git a/api/test/context/CMakeLists.txt b/api/test/context/CMakeLists.txt index ea5198fd90..2c9da11191 100644 --- a/api/test/context/CMakeLists.txt +++ b/api/test/context/CMakeLists.txt @@ -2,8 +2,9 @@ include(GoogleTest) foreach(testname context_test runtime_context_test) add_executable(${testname} "${testname}.cc") - target_link_libraries(${testname} ${GTEST_BOTH_LIBRARIES} - ${CMAKE_THREAD_LIBS_INIT} opentelemetry_api) + target_link_libraries( + ${testname} ${GTEST_BOTH_LIBRARIES} ${CORE_RUNTIME_LIBS} + ${CMAKE_THREAD_LIBS_INIT} opentelemetry_api) gtest_add_tests( TARGET ${testname} TEST_PREFIX context. diff --git a/api/test/core/CMakeLists.txt b/api/test/core/CMakeLists.txt index 7fead491e5..0d59831bb4 100644 --- a/api/test/core/CMakeLists.txt +++ b/api/test/core/CMakeLists.txt @@ -1,8 +1,9 @@ include(GoogleTest) add_executable(timestamp_test timestamp_test.cc) -target_link_libraries(timestamp_test ${GTEST_BOTH_LIBRARIES} - ${CMAKE_THREAD_LIBS_INIT} opentelemetry_api) +target_link_libraries( + timestamp_test ${GTEST_BOTH_LIBRARIES} ${CORE_RUNTIME_LIBS} + ${CMAKE_THREAD_LIBS_INIT} opentelemetry_api) gtest_add_tests( TARGET timestamp_test TEST_PREFIX trace. diff --git a/api/test/metrics/CMakeLists.txt b/api/test/metrics/CMakeLists.txt index 9d70c376ef..f20a46c094 100644 --- a/api/test/metrics/CMakeLists.txt +++ b/api/test/metrics/CMakeLists.txt @@ -1,7 +1,8 @@ foreach(testname noop_instrument_test meter_provider_test noop_metrics_test) add_executable(${testname} "${testname}.cc") - target_link_libraries(${testname} ${GTEST_BOTH_LIBRARIES} - ${CMAKE_THREAD_LIBS_INIT} opentelemetry_api) + target_link_libraries( + ${testname} ${GTEST_BOTH_LIBRARIES} ${CORE_RUNTIME_LIBS} + ${CMAKE_THREAD_LIBS_INIT} opentelemetry_api) gtest_add_tests( TARGET ${testname} TEST_PREFIX metrics. diff --git a/api/test/nostd/CMakeLists.txt b/api/test/nostd/CMakeLists.txt index 1fb120736e..21c2197ced 100644 --- a/api/test/nostd/CMakeLists.txt +++ b/api/test/nostd/CMakeLists.txt @@ -3,8 +3,9 @@ include(GoogleTest) foreach(testname function_ref_test string_view_test unique_ptr_test utility_test span_test shared_ptr_test) add_executable(${testname} "${testname}.cc") - target_link_libraries(${testname} ${GTEST_BOTH_LIBRARIES} - ${CMAKE_THREAD_LIBS_INIT} opentelemetry_api) + target_link_libraries( + ${testname} ${GTEST_BOTH_LIBRARIES} ${CORE_RUNTIME_LIBS} + ${CMAKE_THREAD_LIBS_INIT} opentelemetry_api) gtest_add_tests( TARGET ${testname} TEST_PREFIX nostd. diff --git a/api/test/nostd/shared_ptr_test.cc b/api/test/nostd/shared_ptr_test.cc index f55d47936f..f24b8c6946 100644 --- a/api/test/nostd/shared_ptr_test.cc +++ b/api/test/nostd/shared_ptr_test.cc @@ -159,11 +159,11 @@ TEST(SharedPtrTest, Comparison) EXPECT_EQ(nullptr, ptr3); } -TEST(SharedPtrTest, Sort) +static void SharedPtrTest_Sort(size_t size = 10) { std::vector> nums; - for (int i = 10; i > 0; i--) + for (int i = size; i > 0; i--) { nums.push_back(shared_ptr(new int(i))); } @@ -179,3 +179,8 @@ TEST(SharedPtrTest, Sort) EXPECT_EQ(nums, nums2); } + +TEST(SharedPtrTest, Sort) +{ + SharedPtrTest_Sort(); +} diff --git a/api/test/nostd/span_test.cc b/api/test/nostd/span_test.cc index 44e7d49a15..8bfbf60f29 100644 --- a/api/test/nostd/span_test.cc +++ b/api/test/nostd/span_test.cc @@ -1,5 +1,7 @@ #include "opentelemetry/nostd/span.h" +#include + #include #include #include @@ -56,7 +58,10 @@ TEST(SpanTest, PointerCountConstruction) EXPECT_EQ(s2.data(), array.data()); EXPECT_EQ(s2.size(), array.size()); +#ifndef HAVE_CPP_STDLIB + /* This test is not supposed to fail with STL. Why is this invalid construct? */ EXPECT_DEATH((span{array.data(), array.size()}), ".*"); +#endif } TEST(SpanTest, RangeConstruction) @@ -71,7 +76,10 @@ TEST(SpanTest, RangeConstruction) EXPECT_EQ(s2.data(), array); EXPECT_EQ(s2.size(), 3); +#ifndef HAVE_CPP_STDLIB + /* This test is not supposed to fail with STL. Why is this invalid construct? */ EXPECT_DEATH((span{std::begin(array), std::end(array)}), ".*"); +#endif } TEST(SpanTest, ArrayConstruction) @@ -106,10 +114,15 @@ TEST(SpanTest, ContainerConstruction) EXPECT_EQ(s1.data(), v.data()); EXPECT_EQ(s1.size(), v.size()); - span s2{v}; + span s2{v.data(), 3}; + EXPECT_EQ(s2.data(), v.data()); EXPECT_EQ(s2.size(), v.size()); - EXPECT_DEATH((span{v}), ".*"); + +#ifndef HAVE_CPP_STDLIB + /* This test is not supposed to fail with STL. Why is this invalid construct? */ + EXPECT_DEATH((span{v.data(), 3}), ".*"); +#endif EXPECT_FALSE((std::is_constructible, std::vector>::value)); EXPECT_FALSE((std::is_constructible, std::list>::value)); diff --git a/api/test/nostd/string_view_test.cc b/api/test/nostd/string_view_test.cc index 364410a312..3673082cca 100644 --- a/api/test/nostd/string_view_test.cc +++ b/api/test/nostd/string_view_test.cc @@ -2,7 +2,7 @@ #include -#include "map" +#include using opentelemetry::nostd::string_view; @@ -68,7 +68,7 @@ TEST(StringViewTest, SubstrPortion) TEST(StringViewTest, SubstrOutOfRange) { string_view s = "abc123"; -#if __EXCEPTIONS +#if __EXCEPTIONS || defined(HAVE_STDLIB_CPP) EXPECT_THROW(s.substr(10), std::out_of_range); #else EXPECT_DEATH({ s.substr(10); }, ""); diff --git a/api/test/plugin/CMakeLists.txt b/api/test/plugin/CMakeLists.txt index 6bdcbf9c32..29a75b6b84 100644 --- a/api/test/plugin/CMakeLists.txt +++ b/api/test/plugin/CMakeLists.txt @@ -1,8 +1,9 @@ include(GoogleTest) add_executable(dynamic_load_test dynamic_load_test.cc) -target_link_libraries(dynamic_load_test ${GTEST_BOTH_LIBRARIES} - ${CMAKE_THREAD_LIBS_INIT} opentelemetry_api) +target_link_libraries( + dynamic_load_test ${GTEST_BOTH_LIBRARIES} ${CORE_RUNTIME_LIBS} + ${CMAKE_THREAD_LIBS_INIT} opentelemetry_api) target_link_libraries(dynamic_load_test ${CMAKE_DL_LIBS}) gtest_add_tests( TARGET dynamic_load_test diff --git a/api/test/trace/CMakeLists.txt b/api/test/trace/CMakeLists.txt index 0faac5cfb4..6c69883b2c 100644 --- a/api/test/trace/CMakeLists.txt +++ b/api/test/trace/CMakeLists.txt @@ -10,8 +10,9 @@ foreach( noop_test tracer_test) add_executable(api_${testname} "${testname}.cc") - target_link_libraries(api_${testname} ${GTEST_BOTH_LIBRARIES} - ${CMAKE_THREAD_LIBS_INIT} opentelemetry_api) + target_link_libraries( + api_${testname} ${GTEST_BOTH_LIBRARIES} ${CORE_RUNTIME_LIBS} + ${CMAKE_THREAD_LIBS_INIT} opentelemetry_api) gtest_add_tests( TARGET api_${testname} TEST_PREFIX trace. diff --git a/api/test/trace/propagation/CMakeLists.txt b/api/test/trace/propagation/CMakeLists.txt index ff707348bd..8f13cd68de 100644 --- a/api/test/trace/propagation/CMakeLists.txt +++ b/api/test/trace/propagation/CMakeLists.txt @@ -1,7 +1,8 @@ foreach(testname http_text_format_test) add_executable(${testname} "${testname}.cc") - target_link_libraries(${testname} ${GTEST_BOTH_LIBRARIES} - ${CMAKE_THREAD_LIBS_INIT} opentelemetry_api) + target_link_libraries( + ${testname} ${GTEST_BOTH_LIBRARIES} ${CORE_RUNTIME_LIBS} + ${CMAKE_THREAD_LIBS_INIT} opentelemetry_api) gtest_add_tests( TARGET ${testname} TEST_PREFIX trace. diff --git a/ci/do_ci.sh b/ci/do_ci.sh index 53fa808027..36708021ec 100755 --- a/ci/do_ci.sh +++ b/ci/do_ci.sh @@ -33,6 +33,9 @@ elif [[ "$1" == "cmake.c++20.test" ]]; then elif [[ "$1" == "cmake.legacy.test" ]]; then cd "${BUILD_DIR}" rm -rf * + export BUILD_ROOT="${BUILD_DIR}" + ${SRC_DIR}/tools/build-gtest.sh + ${SRC_DIR}/tools/build-benchmark.sh cmake -DCMAKE_BUILD_TYPE=Debug \ -DCMAKE_CXX_FLAGS="-Werror" \ -DCMAKE_CXX_STANDARD=11 \ diff --git a/docker/ubuntu14.04/Dockerfile b/docker/ubuntu14.04/Dockerfile index 9f875f453c..f4e8a04104 100644 --- a/docker/ubuntu14.04/Dockerfile +++ b/docker/ubuntu14.04/Dockerfile @@ -8,7 +8,7 @@ RUN apt-get -y update && apt-get -y upgrade && apt-get -y dist-upgrade RUN apt-get install -qq -y --ignore-missing \ apt-utils \ automake \ - bc \ + bc \ build-essential \ bzip2 \ cmake \ diff --git a/docker/ubuntu16.04/Dockerfile b/docker/ubuntu16.04/Dockerfile index 62f8c64319..328600f1cb 100644 --- a/docker/ubuntu16.04/Dockerfile +++ b/docker/ubuntu16.04/Dockerfile @@ -9,7 +9,7 @@ RUN apt-get install -qq -y --ignore-missing \ apt-utils \ automake \ build-essential \ - bc \ + bc \ bzip2 \ cmake \ curl \ diff --git a/docker/ubuntu18.04/Dockerfile b/docker/ubuntu18.04/Dockerfile index b3f28986cf..9e41506b4f 100644 --- a/docker/ubuntu18.04/Dockerfile +++ b/docker/ubuntu18.04/Dockerfile @@ -8,7 +8,7 @@ RUN apt-get -y update && apt-get -y upgrade && apt-get -y dist-upgrade RUN apt-get install -qq -y --ignore-missing \ apt-utils \ automake \ - bc \ + bc \ build-essential \ bzip2 \ cmake \ diff --git a/docker/ubuntu20.04/Dockerfile b/docker/ubuntu20.04/Dockerfile index 84966fae66..17dba84799 100644 --- a/docker/ubuntu20.04/Dockerfile +++ b/docker/ubuntu20.04/Dockerfile @@ -8,7 +8,7 @@ RUN apt-get -y update && apt-get -y upgrade && apt-get -y dist-upgrade RUN apt-get install -qq -y --ignore-missing \ apt-utils \ automake \ - bc \ + bc \ build-essential \ bzip2 \ cmake \ diff --git a/docs/abi-compatibility.md b/docs/abi-compatibility.md new file mode 100644 index 0000000000..53936de0bd --- /dev/null +++ b/docs/abi-compatibility.md @@ -0,0 +1,35 @@ +# Windows + +## Visual Studio 2015, 2017, 2019 + +The Microsoft C++ (MSVC) compiler toolsets in Visual Studio 2013 and earlier don't guarantee binary compatibility across versions. You can't link object files, static libraries, dynamic libraries, and executables built by different versions. The ABIs, object formats, and runtime libraries are incompatible. + +We've changed this behavior in Visual Studio 2015, 2017, and 2019. The runtime libraries and apps compiled by any of these versions of the compiler are binary-compatible. It's reflected in the C++ toolset major number, which is 14 for all three versions. (The toolset version is v140 for Visual Studio 2015, v141 for 2017, and v142 for 2019). Say you have third-party libraries built by Visual Studio 2015. You can still use them in an application built by Visual Studio 2017 or 2019. There's no need to recompile with a matching toolset. The latest version of the Microsoft Visual C++ Redistributable package (the Redistributable) works for all of them. + +There are three important restrictions on binary compatibility: + +- You can mix binaries built by different versions of the toolset. However, you must use a toolset at least as recent as the most recent binary to link your app. Here's an example: you can link an app compiled using the 2017 toolset to a static library compiled using 2019, if they're linked using the 2019 toolset. + +- The Redistributable your app uses has a similar binary-compatibility restriction. When you mix binaries built by different supported versions of the toolset, the Redistributable version must be at least as new as the latest toolset used by any app component. + +- Static libraries or object files compiled using the /GL (Whole program optimization) compiler switch aren't binary-compatible across versions. All object files and libraries compiled using /GL must use exactly the same toolset for the compile and the final link. + +[Reference](https://docs.microsoft.com/en-us/cpp/porting/binary-compat-2015-2017?view=vs-2019) + +## Breaking Compatibility + +### Exceptions + +- [__CxxFrameHandler4](https://devblogs.microsoft.com/cppblog/making-cpp-exception-handling-smaller-x64/) - static library built with Visual Studio 2019 wont link to executable compiled with Visual Studio 2017 + +### Release vs Debug libraries on Windows + +- [\_SECURE_SCL](https://docs.microsoft.com/en-us/cpp/standard-library/secure-scl?view=vs-2019) - checked iterators (old) + +- [\_ITERATOR_DEBUG_LEVEL](https://docs.microsoft.com/en-us/cpp/standard-library/iterator-debug-level?view=vs-2019) - checked iterators (new) + +### Spectre Mitigation + +- [Runtime Libraries for Spectre Mitigation](https://docs.microsoft.com/en-us/cpp/build/reference/qspectre?view=vs-2019) + +- [New Spectre Mitigations in Visual Studio 2019](https://devblogs.microsoft.com/cppblog/more-spectre-mitigations-in-msvc/) diff --git a/docs/building-with-stdlib.md b/docs/building-with-stdlib.md new file mode 100644 index 0000000000..ed0911aa1a --- /dev/null +++ b/docs/building-with-stdlib.md @@ -0,0 +1,191 @@ +# Building with Standard C++ Library + +Standard Library build flavor works best for statically linking the SDK in a +process (environment where ABI compat is not a requirement), or for +"header-only" implementation of SDK. + +Proposed approach cannot be employed for shared libs in environments where +ABI compatibility is required. OpenTelemetry SDK binary compiled with +`compiler A + STL B` will not be ABI -compatible with the main executable +compiled with `compiler C + STL D`. + +In addition to standard library, similar approach can be reused to implement +the API surface classes with [Abseil classes](https://abseil.io/) instead of +`nostd`, in products that prefer Abseil. + +## Motivation + +`nostd` classes in OpenTelemetry API were introduced for the following reasons: +- ABI stability: scenario where different modules are compiled with different +compiler and incompatible standard library. +- backport of C++17 and above features to C++11 compiler. + +The need for custom `nostd` classes is significantly diminished when the SDK is +compiled with C++17 or above compiler. Only `std::span` needs to be backported. + +Subsequently, there is no need for `nostd` classes in environment with C++20 or +above compiler, where all system modules are compiled with the same / compatible +compiler. And where the same standard library implementation is being used. This +is the case when SDK is compiled into product itself, with no runtime loadable +components. + +Compiling OpenTelemetry SDK from source using standard library classes: +`std::map`, `std::string_view`, `std::span`, `std::variant` +instead of `nostd::` yields better performance and debugability at expense +of potentially losing ABI compatibility. However, the standard library built +for Release is guaranteed to be compatible across Visual Studio 2015, 2017 and +2019 compilers on Windows with vc14x runtime. Thus, ABI stability requirement +introduces an additional unnecessary runtime complexity and overhead. + +While we are committed to support `nostd` classes for those environments where +ABI compatibility is a requirement, we would also like to add flexibility to +build system to optimize the SDK for the case where ABI compatibility is NOT +a requirement. + +Implementation of this feature can be subsequently be used as a foundation for +further work - allow bindings to [Abseil](https://github.com/abseil/abseil-cpp) +"backport" implementation of the standard library. + +Implementation is completely opaque from SDK code / SDK developer perspective: +mapping / aliasing from `nostd::` classes back to their `std::` counterparts +is done in a corresponding `opentelemetry/nostd/*.h` header. Users still use +`nostd` classes, but the most optimal implementation is picked up depending on +whether users require ABI stability or not. + +Example environments that contain the full set of standard classes: +- C++17 or above compiler, with Microsoft GSL backport of `gsl::span` +- C++20 compilers: Visual Studio 2019+, latest LLVM clang, latest gcc + +We continue fully supporting both models (`nostd`, `stdlib`) by running CI for both. + +## Implementation + +Allow to alias from `nostd::` to `std::` classes for C++17 and above. + +Consistent handling of `std::variant` across various OS: + +- backport of a few missing variant features, e.g. `std::get` and `std::visit` + for older version of Mac OS X. Patches that enable proper handling of + `std::visit` and `std::variant` irrespective of OS version to resolve + [this quirk](https://stackoverflow.com/questions/52310835/xcode-10-call-to-unavailable-function-stdvisit). + +- ability to borrow implementation of C++20 `gsl::span` from + [Microsoft Guidelines Support Library](https://github.com/microsoft/GSL). + This is necessary for C++17 and above compiler. + +- ability to use Abseil classes for Visual Studio 2015 :`nostd::variant` does + not compile with Visual Studio 2010. Please refer to [this issue](https://github.com/open-telemetry/opentelemetry-cpp/issues/314) + +## Pros and Cons + +### Using trusted/certified Standard Library + +Using standard library implementation classes in customer code and on API +surface instead of `nostd` classes. Certain environments would prefer to have +**standard** instead of non-standard classes due to security, performance, +stability and debug'ability considerations. For example, Visual Studio IDE +provides 1st class Debug experience for Standard containers, plus additional +runtime-checks for Debug builds that use Standard containers. + +### Minimizing binary size + +No need to marshal types from standard to `nostd`, then back to standard +library across ABI boundary - means less code involved and less memcpy. We use +Standard Library classes used elsewhere in the app. We can optimize the +event passing by avoiding `KeyValueIterable` transform (and, thus, unnecessary memcpy) +when we know that the incoming container type matches that one used by SDK. + +### Avoiding unnecessary extra memcpy (perf improvements) + +No need to transform from 'native' standard library types, e.g. `std::map` via +`KeyValueIterable` means we can bypass that transformation process, if and when +we know that the ABI compatibility is not a requirement in certain environment. +ETA perf improvement is 1.5%-3% better perf since an extra transform, memcpy, +iteration for-each key-value is avoided. Custom OpenTelemetry SDK implementation +may operate on standard container classes rather than doing transform from one +container type to another. + +### ABI stability + +Obviously this approach does not work in Linux environments, where ABI stability +guarantee must be provided, e.g. for dynamically loadable OpenTelemetry SDK +module and plugins for products such as NGINX, Envoy, etc. Example: a product is +compiled with modern gcc compiler. But OpenTelemetry SDK is compiled with an +older runtime library. In this case the SDK must be compiled with `nostd`. + +Note that for most scenarios with modern Windows compilers, STL library is +ABI-safe across Visual Studio 2015, 2017 and 2019 with vc14x runtime. + +Quote from [official documentation](https://docs.microsoft.com/en-us/cpp/porting/binary-compat-2015-2017?view=msvc-160) : + +Visual Studio 2015, 2017, and 2019: the runtime libraries and apps compiled by +any of these versions of the compiler are binary-compatible. It's reflected in +the C++ toolset major number, which is 14 for all three versions. The toolset +version is v140 for Visual Studio 2015, v141 for 2017, and v142 for 2019. +Say you have third-party libraries built by Visual Studio 2015. You can still +use them in an application built by Visual Studio 2017 or 2019. There's no need +to recompile with a matching toolset. The latest version of the Microsoft Visual +C++ Redistributable package (the Redistributable) works for all of them. + +Visual Studio provides 1st class debug experience for the standard library. +## Build and Test considerations + +### Separate flavors of SDK build + +Supported build flavors: + +- `nostd` - OpenTelemetry backport of classes for C++11. Not using standard lib. +- `stdlib` - Standard Library. Full native experience with C++20 compiler. + C++17 works but with additional dependencies, e.g. either MS-GSL or Abseil + for `std::span` implementation (`gsl::span` or `absl::Span`). +- `absl` - TODO: this should allow using Abseil C++ library only (no MS-GSL). + +Currently only `nostd` and `stdlib` configurations are implemented in CMake build. +`absl` is reserved for future use. Build systems other than CMake need to +`#define HAVE_CPP_STDLIB` to enable the Standard Library classes. + +### Build matrix + +List of compilers that support building with standard library classes: + +Compiler | Language standard | Notes +-------------------|-------------------|------------------- +Visual Studio 2015 | C++14 | can only be built with `nostd` flavor +Visual Studio 2017 | C++17 | requires `gsl::span` for `std::span` implementation +Visual Studio 2019 | C++20 | +Xcode 11.x | C++17 | requires `gsl::span` for `std::span` implementation +Xcode 12.x | C++20 | +gcc-7 | C++17 | requires `gsl::span` for `std::span` implementation +gcc-9+ | C++20 | + +C++20 `std::span` -compatible implementation is needed for C++17 compilers. + +Other modern C++ language features used by OpenTelemetry, e.g. `std::string_view` +and `std::variant` are available in C++17 standard library. Minor customization +needed for Apple LLVM clang in `std::variant` exception handling due to the fact +that the variant exception handler presence depends on what OS version developer +is targeting. This exception on Apple systems is implemented inside the OS system +library. This idiosyncrasy is now handled by OpenTelemetry API in opaque manner: +support `nostd::variant` exception handling even on older Mac OS X and iOS by +providing implementation of it in OpenTelemetry SDK: if exceptions are enabled, +then throw `nostd::bad_variant_access` exception old OS where `std::bad_variant_access` +is unavailable. + +#### Note on `gsl::span` vs `absl::Span` + +It is important to note that, while `absl::Span` is similar in design and +purpose to the `std::span` (and existing `gsl::span` reference implementation), +`absl::Span` is not currently guaranteeing to be a drop-in replacement for any +eventual standard. Instead, `absl::Span` aims to have an interface as similar +as possible to `absl::string_view`, without the string-specific functionality. + +Thus, OpenTelemetry built with standard library prefers `gsl::span` as it is +fully compatible with standard `std::span`. It may become possible to use Abseil +classes should we confirm that its behavior is consistent with `nostd::span` +expectations. + +### Test setup + +CI allows to validate that all OpenTelemetry functionality is working the same +identical way irrespective of what C++ runtime / STL library it is compiled +with. diff --git a/examples/batch/CMakeLists.txt b/examples/batch/CMakeLists.txt index d1146d59b3..41654aeb93 100644 --- a/examples/batch/CMakeLists.txt +++ b/examples/batch/CMakeLists.txt @@ -2,5 +2,6 @@ include_directories(${CMAKE_SOURCE_DIR}/exporters/ostream/include) add_executable(batch_span_processor_example main.cc) -target_link_libraries(batch_span_processor_example ${CMAKE_THREAD_LIBS_INIT} - opentelemetry_exporter_ostream_span opentelemetry_trace) +target_link_libraries( + batch_span_processor_example ${CMAKE_THREAD_LIBS_INIT} ${CORE_RUNTIME_LIBS} + opentelemetry_exporter_ostream_span opentelemetry_trace) diff --git a/examples/metrics_simple/CMakeLists.txt b/examples/metrics_simple/CMakeLists.txt index 4269318871..add3a153ce 100644 --- a/examples/metrics_simple/CMakeLists.txt +++ b/examples/metrics_simple/CMakeLists.txt @@ -3,4 +3,4 @@ include_directories(${CMAKE_SOURCE_DIR}/exporters/ostream/include) add_executable(simple_metrics main.cc) target_link_libraries( simple_metrics ${CMAKE_THREAD_LIBS_INIT} opentelemetry_metrics - opentelemetry_exporter_ostream_metrics) + ${CORE_RUNTIME_LIBS} opentelemetry_exporter_ostream_metrics) diff --git a/examples/otlp/CMakeLists.txt b/examples/otlp/CMakeLists.txt index 8c5bf92490..8b77baad38 100644 --- a/examples/otlp/CMakeLists.txt +++ b/examples/otlp/CMakeLists.txt @@ -4,9 +4,14 @@ include_directories(${CMAKE_SOURCE_DIR}/exporters/otlp/include) add_library(otlp_foo_library foo_library/foo_library.cc) target_link_libraries(otlp_foo_library ${CMAKE_THREAD_LIBS_INIT} - opentelemetry_api) + ${CORE_RUNTIME_LIBS} opentelemetry_api) add_executable(example_otlp main.cc) target_link_libraries( - example_otlp ${CMAKE_THREAD_LIBS_INIT} otlp_foo_library opentelemetry_trace - opentelemetry_exporter_otprotocol gRPC::grpc++) + example_otlp + ${CMAKE_THREAD_LIBS_INIT} + otlp_foo_library + opentelemetry_trace + ${CORE_RUNTIME_LIBS} + opentelemetry_exporter_otprotocol + gRPC::grpc++) diff --git a/examples/plugin/plugin/tracer.cc b/examples/plugin/plugin/tracer.cc index cd7d19a1e0..68478c5ce1 100644 --- a/examples/plugin/plugin/tracer.cc +++ b/examples/plugin/plugin/tracer.cc @@ -1,5 +1,4 @@ #include "tracer.h" -#include "opentelemetry/context/runtime_context.h" #include "opentelemetry/nostd/unique_ptr.h" #include diff --git a/exporters/ostream/CMakeLists.txt b/exporters/ostream/CMakeLists.txt index e870bb09cf..909ce7fdbe 100644 --- a/exporters/ostream/CMakeLists.txt +++ b/exporters/ostream/CMakeLists.txt @@ -11,11 +11,11 @@ if(BUILD_TESTING) target_link_libraries( ostream_span_test ${GTEST_BOTH_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} - opentelemetry_exporter_ostream_span) + ${CORE_RUNTIME_LIBS} opentelemetry_exporter_ostream_span) target_link_libraries( ostream_metrics_test ${GTEST_BOTH_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} - opentelemetry_exporter_ostream_metrics) + ${CORE_RUNTIME_LIBS} opentelemetry_exporter_ostream_metrics) target_link_libraries( ostream_log_test ${GTEST_BOTH_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} diff --git a/exporters/otlp/src/recordable.cc b/exporters/otlp/src/recordable.cc index 82afe7b90b..44d04082b4 100644 --- a/exporters/otlp/src/recordable.cc +++ b/exporters/otlp/src/recordable.cc @@ -59,6 +59,12 @@ void PopulateAttribute(opentelemetry::proto::common::v1::KeyValue *attribute, attribute->mutable_value()->set_string_value(nostd::get(value).data(), nostd::get(value).size()); } +#ifdef HAVE_CSTRING_TYPE + else if (nostd::holds_alternative(value)) + { + attribute->mutable_value()->set_string_value(nostd::get(value)); + } +#endif else if (nostd::holds_alternative>(value)) { for (const auto &val : nostd::get>(value)) diff --git a/ext/include/opentelemetry/ext/http/server/socket_tools.h b/ext/include/opentelemetry/ext/http/server/socket_tools.h index 4e381f3ce1..095a7eeeff 100644 --- a/ext/include/opentelemetry/ext/http/server/socket_tools.h +++ b/ext/include/opentelemetry/ext/http/server/socket_tools.h @@ -96,7 +96,6 @@ struct Thread { std::thread m_thread; - // volatile bool m_terminate{false}; std::atomic m_terminate{false}; /// diff --git a/sdk/include/opentelemetry/sdk/logs/simple_log_processor.h b/sdk/include/opentelemetry/sdk/logs/simple_log_processor.h index 38a4194c16..d387e3c7fc 100644 --- a/sdk/include/opentelemetry/sdk/logs/simple_log_processor.h +++ b/sdk/include/opentelemetry/sdk/logs/simple_log_processor.h @@ -59,7 +59,7 @@ class SimpleLogProcessor : public LogProcessor // The lock used to ensure the exporter is not called concurrently opentelemetry::common::SpinLockMutex lock_; // The atomic boolean flag to ensure the ShutDown() function is only called once - std::atomic_flag shutdown_latch_{ATOMIC_FLAG_INIT}; + std::atomic_flag shutdown_latch_ = ATOMIC_FLAG_INIT; }; } // namespace logs } // namespace sdk diff --git a/sdk/include/opentelemetry/sdk/trace/attribute_utils.h b/sdk/include/opentelemetry/sdk/trace/attribute_utils.h index 2b1c082441..821ad953fa 100644 --- a/sdk/include/opentelemetry/sdk/trace/attribute_utils.h +++ b/sdk/include/opentelemetry/sdk/trace/attribute_utils.h @@ -1,5 +1,6 @@ #pragma once +#include #include #include #include "opentelemetry/common/attribute_value.h" @@ -21,6 +22,12 @@ using SpanDataAttributeValue = nostd::variant, +#endif std::vector, std::vector, std::vector, @@ -37,17 +44,23 @@ struct AttributeConverter SpanDataAttributeValue operator()(bool v) { return SpanDataAttributeValue(v); } SpanDataAttributeValue operator()(int32_t v) { return SpanDataAttributeValue(v); } SpanDataAttributeValue operator()(uint32_t v) { return SpanDataAttributeValue(v); } - /*SpanDataAttributeValue operator()(int v) - { - return SpanDataAttributeValue(static_cast(v)); - }*/ SpanDataAttributeValue operator()(int64_t v) { return SpanDataAttributeValue(v); } SpanDataAttributeValue operator()(uint64_t v) { return SpanDataAttributeValue(v); } SpanDataAttributeValue operator()(double v) { return SpanDataAttributeValue(v); } SpanDataAttributeValue operator()(nostd::string_view v) { - return SpanDataAttributeValue(std::string(v)); + return SpanDataAttributeValue(std::string(v.data(), v.size())); + } + SpanDataAttributeValue operator()(const char *s) + { + return SpanDataAttributeValue(std::string(s)); } +#ifdef HAVE_SPAN_BYTE + SpanDataAttributeValue operator()(nostd::span v) + { + return convertSpan(v); + } +#endif SpanDataAttributeValue operator()(nostd::span v) { return convertSpan(v); } SpanDataAttributeValue operator()(nostd::span v) { @@ -74,12 +87,7 @@ struct AttributeConverter template SpanDataAttributeValue convertSpan(nostd::span vals) { - std::vector copy; - for (auto &val : vals) - { - copy.push_back(T(val)); - } - + const std::vector copy(vals.begin(), vals.end()); return SpanDataAttributeValue(std::move(copy)); } }; diff --git a/sdk/include/opentelemetry/sdk/trace/simple_processor.h b/sdk/include/opentelemetry/sdk/trace/simple_processor.h index aec0d51cc6..7ad5848c07 100644 --- a/sdk/include/opentelemetry/sdk/trace/simple_processor.h +++ b/sdk/include/opentelemetry/sdk/trace/simple_processor.h @@ -72,7 +72,7 @@ class SimpleSpanProcessor : public SpanProcessor private: std::unique_ptr exporter_; opentelemetry::common::SpinLockMutex lock_; - std::atomic_flag shutdown_latch_{ATOMIC_FLAG_INIT}; + std::atomic_flag shutdown_latch_ = ATOMIC_FLAG_INIT; }; } // namespace trace } // namespace sdk diff --git a/sdk/include/opentelemetry/sdk/trace/span_data.h b/sdk/include/opentelemetry/sdk/trace/span_data.h index 24796d65d7..b5594804fe 100644 --- a/sdk/include/opentelemetry/sdk/trace/span_data.h +++ b/sdk/include/opentelemetry/sdk/trace/span_data.h @@ -13,6 +13,8 @@ #include "opentelemetry/trace/span_id.h" #include "opentelemetry/trace/trace_id.h" +#include + OPENTELEMETRY_BEGIN_NAMESPACE namespace sdk { @@ -201,7 +203,10 @@ class SpanData final : public Recordable status_desc_ = std::string(description); } - void SetName(nostd::string_view name) noexcept override { name_ = std::string(name); } + void SetName(nostd::string_view name) noexcept override + { + name_ = std::string(name.data(), name.length()); + } void SetSpanKind(opentelemetry::trace::SpanKind span_kind) noexcept override { diff --git a/sdk/src/common/BUILD b/sdk/src/common/BUILD index a8981b4d55..b0724c3810 100644 --- a/sdk/src/common/BUILD +++ b/sdk/src/common/BUILD @@ -16,7 +16,10 @@ package(default_visibility = ["//visibility:public"]) cc_library( name = "random", - srcs = ["random.cc"], + srcs = [ + "core.cc", + "random.cc", + ], hdrs = [ "fast_random_number_generator.h", "random.h", diff --git a/sdk/src/common/CMakeLists.txt b/sdk/src/common/CMakeLists.txt index 042a857539..4a47500a73 100644 --- a/sdk/src/common/CMakeLists.txt +++ b/sdk/src/common/CMakeLists.txt @@ -1,4 +1,4 @@ -set(COMMON_SRCS random.cc) +set(COMMON_SRCS random.cc core.cc) if(WIN32) list(APPEND COMMON_SRCS platform/fork_windows.cc) else() @@ -6,4 +6,5 @@ else() endif() add_library(opentelemetry_common ${COMMON_SRCS}) -target_link_libraries(opentelemetry_common Threads::Threads) +target_link_libraries(opentelemetry_common Threads::Threads + ${CORE_RUNTIME_LIBS}) diff --git a/sdk/src/common/core.cc b/sdk/src/common/core.cc new file mode 100644 index 0000000000..a8db9a0448 --- /dev/null +++ b/sdk/src/common/core.cc @@ -0,0 +1,12 @@ +#include "opentelemetry/nostd/variant.h" +#include "opentelemetry/version.h" + +#if defined(HAVE_ABSEIL) +namespace absl +{ +namespace variant_internal +{ +void __cdecl ThrowBadVariantAccess(){/* TODO: std::terminate or re-throw? */}; +} +} // namespace absl +#endif diff --git a/sdk/src/trace/CMakeLists.txt b/sdk/src/trace/CMakeLists.txt index e4d6bdc5ed..97f4f3d3b3 100644 --- a/sdk/src/trace/CMakeLists.txt +++ b/sdk/src/trace/CMakeLists.txt @@ -3,4 +3,5 @@ add_library( tracer_provider.cc tracer.cc span.cc batch_span_processor.cc samplers/parent.cc samplers/trace_id_ratio.cc) -target_link_libraries(opentelemetry_trace opentelemetry_common) +target_link_libraries(opentelemetry_trace opentelemetry_common + ${CORE_RUNTIME_LIBS}) diff --git a/sdk/test/common/CMakeLists.txt b/sdk/test/common/CMakeLists.txt index 5c6ffe868c..f1028bf6d8 100644 --- a/sdk/test/common/CMakeLists.txt +++ b/sdk/test/common/CMakeLists.txt @@ -4,7 +4,7 @@ foreach(testname add_executable(${testname} "${testname}.cc") target_link_libraries( ${testname} ${GTEST_BOTH_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} - opentelemetry_common opentelemetry_trace) + ${CORE_RUNTIME_LIBS} opentelemetry_common opentelemetry_trace) gtest_add_tests( TARGET ${testname} TEST_PREFIX trace. @@ -16,9 +16,11 @@ target_link_libraries(random_fork_test opentelemetry_common) add_test(random_fork_test random_fork_test) add_executable(random_benchmark random_benchmark.cc) -target_link_libraries(random_benchmark benchmark::benchmark - ${CMAKE_THREAD_LIBS_INIT} opentelemetry_common) +target_link_libraries( + random_benchmark benchmark::benchmark ${CORE_RUNTIME_LIBS} + ${CMAKE_THREAD_LIBS_INIT} opentelemetry_common) add_executable(circular_buffer_benchmark circular_buffer_benchmark.cc) -target_link_libraries(circular_buffer_benchmark benchmark::benchmark - ${CMAKE_THREAD_LIBS_INIT} opentelemetry_api) +target_link_libraries( + circular_buffer_benchmark benchmark::benchmark ${CORE_RUNTIME_LIBS} + ${CMAKE_THREAD_LIBS_INIT} opentelemetry_api) diff --git a/sdk/test/trace/CMakeLists.txt b/sdk/test/trace/CMakeLists.txt index 747ec96f31..b68a57f9cc 100644 --- a/sdk/test/trace/CMakeLists.txt +++ b/sdk/test/trace/CMakeLists.txt @@ -12,8 +12,13 @@ foreach( attribute_utils_test) add_executable(${testname} "${testname}.cc") target_link_libraries( - ${testname} ${GTEST_BOTH_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} - opentelemetry_common opentelemetry_trace opentelemetry_exporter_in_memory) + ${testname} + ${CORE_RUNTIME_LIBS} + ${GTEST_BOTH_LIBRARIES} + ${CMAKE_THREAD_LIBS_INIT} + opentelemetry_common + opentelemetry_trace + opentelemetry_exporter_in_memory) gtest_add_tests( TARGET ${testname} TEST_PREFIX trace. @@ -23,4 +28,4 @@ endforeach() add_executable(sampler_benchmark sampler_benchmark.cc) target_link_libraries( sampler_benchmark benchmark::benchmark ${CMAKE_THREAD_LIBS_INIT} - opentelemetry_trace opentelemetry_exporter_in_memory) + ${CORE_RUNTIME_LIBS} opentelemetry_trace opentelemetry_exporter_in_memory) diff --git a/third_party/benchmark b/third_party/benchmark index 348aa2c964..bf585a2789 160000 --- a/third_party/benchmark +++ b/third_party/benchmark @@ -1 +1 @@ -Subproject commit 348aa2c964494b5947c0e7f96b82c1fe844d684f +Subproject commit bf585a2789e30585b4e3ce6baf11ef2750b54677 diff --git a/third_party/opentelemetry-proto b/third_party/opentelemetry-proto index f11e0538fd..286810dc20 160000 --- a/third_party/opentelemetry-proto +++ b/third_party/opentelemetry-proto @@ -1 +1 @@ -Subproject commit f11e0538fd7dc30127ca6bfb2062e5d9f782b77b +Subproject commit 286810dc20d40f6483abf719f2b8de28f543fc78 diff --git a/tools/WORKSPACE b/tools/WORKSPACE new file mode 100644 index 0000000000..abebe189fa --- /dev/null +++ b/tools/WORKSPACE @@ -0,0 +1,4 @@ +local_repository( + name = "vcpkg", + path = "./vcpkg", +) diff --git a/tools/format.sh b/tools/format.sh index 2e9ad3fb54..523cceb75d 100755 --- a/tools/format.sh +++ b/tools/format.sh @@ -6,7 +6,7 @@ fi set -e -FIND="find . -name third_party -prune -o -name tools -prune -o -name .git -prune -o -name _deps -prune -o -name .build -prune -o -name out -prune -o" +FIND="find . -name third_party -prune -o -name tools -prune -o -name .git -prune -o -name _deps -prune -o -name .build -prune -o -name out -prune -o -name .vs -prune -o" # GNU syntax. SED=(sed -i) diff --git a/tools/install.sh b/tools/install.sh old mode 100644 new mode 100755 diff --git a/tools/setup-ninja.sh b/tools/setup-ninja.sh new file mode 100755 index 0000000000..fe3fe9cdfc --- /dev/null +++ b/tools/setup-ninja.sh @@ -0,0 +1,4 @@ +#!/bin/bash +# TODO: add support for Ninja on Mac OS X +wget -O /tmp/ninja.zip https://github.com/ninja-build/ninja/releases/download/v1.10.1/ninja-linux.zip +sudo unzip /tmp/ninja.zip -d /usr/local/bin/ From 903945423e99953046df1d0baf509a6c59690c3a Mon Sep 17 00:00:00 2001 From: Johannes Tax Date: Mon, 21 Dec 2020 22:39:21 -0800 Subject: [PATCH 8/9] Correctly initialize the curl::Session when a schema is given (#476) --- .../ext/http/client/curl/http_client_curl.h | 14 ++++++++++++-- ext/test/http/curl_http_test.cc | 17 +++++++++++++++++ 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/ext/include/opentelemetry/ext/http/client/curl/http_client_curl.h b/ext/include/opentelemetry/ext/http/client/curl/http_client_curl.h index 0605edc9fc..506b6888c9 100644 --- a/ext/include/opentelemetry/ext/http/client/curl/http_client_curl.h +++ b/ext/include/opentelemetry/ext/http/client/curl/http_client_curl.h @@ -110,13 +110,17 @@ class SessionManager; class Session : public http_client::Session { public: - Session(SessionManager &session_manager, std::string host, uint16_t port = 80) + Session(SessionManager &session_manager, const std::string &host, uint16_t port = 80) : session_manager_(session_manager), is_session_active_(false) { if (host.rfind("http://", 0) != 0 && host.rfind("https://", 0) != 0) { host_ = "http://" + host; // TODO - https support } + else + { + host_ = host; + } host_ += ":" + std::to_string(port) + "/"; } @@ -169,6 +173,12 @@ class Session : public http_client::Session void SetId(uint64_t session_id) { session_id_ = session_id; } + /** + * Returns the base URI. + * @return the base URI as a string consisting of scheme, host and port. + */ + const std::string &GetBaseUri() const { return host_; } + private: std::shared_ptr http_request_; std::string host_; @@ -182,7 +192,7 @@ class SessionManager : public http_client::SessionManager { public: // The call (curl_global_init) is not thread safe. Ensure this is called only once. - SessionManager() { curl_global_init(CURL_GLOBAL_ALL); } + SessionManager() : next_session_id_{0} { curl_global_init(CURL_GLOBAL_ALL); } std::shared_ptr CreateSession(nostd::string_view host, uint16_t port = 80) noexcept override diff --git a/ext/test/http/curl_http_test.cc b/ext/test/http/curl_http_test.cc index 70ec4b414e..c5466df6ed 100644 --- a/ext/test/http/curl_http_test.cc +++ b/ext/test/http/curl_http_test.cc @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -261,3 +262,19 @@ TEST_F(BasicCurlHttpTests, CurlHttpOperations) http_operations3.Send(); delete handler; } + +TEST_F(BasicCurlHttpTests, GetBaseUri) +{ + curl::SessionManager session_manager; + + auto session = session_manager.CreateSession("127.0.0.1", 80); + ASSERT_EQ(std::static_pointer_cast(session)->GetBaseUri(), "http://127.0.0.1:80/"); + + session = session_manager.CreateSession("https://127.0.0.1", 443); + ASSERT_EQ(std::static_pointer_cast(session)->GetBaseUri(), + "https://127.0.0.1:443/"); + + session = session_manager.CreateSession("http://127.0.0.1", 31339); + ASSERT_EQ(std::static_pointer_cast(session)->GetBaseUri(), + "http://127.0.0.1:31339/"); +} From ddec444b2a0fcf3b9f22bb238b1f11f7bfc6b6ad Mon Sep 17 00:00:00 2001 From: Tom Tan Date: Mon, 21 Dec 2020 23:59:47 -0800 Subject: [PATCH 9/9] Include CMAKE_TOOLCHAIN_FILE explicitly to enable find_package in vcpkg (#467) --- CMakeLists.txt | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 61db8e332b..026c1de47e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -60,8 +60,6 @@ option(WITH_PROMETHEUS "Whether to include the Prometheus Client in the SDK" option(BUILD_TESTING "Whether to enable tests" ON) option(WITH_EXAMPLES "Whether to build examples" ON) -set(WITH_PROTOBUF OFF) - find_package(Threads) function(install_windows_deps) @@ -71,7 +69,8 @@ function(install_windows_deps) execute_process( COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/tools/setup-buildtools.cmd) set(CMAKE_TOOLCHAIN_FILE - ${CMAKE_CURRENT_SOURCE_DIR}/tools/vcpkg/scripts/buildsystems/vcpkg.cmake) + ${CMAKE_CURRENT_SOURCE_DIR}/tools/vcpkg/scripts/buildsystems/vcpkg.cmake + PARENT_SCOPE) endfunction() if(MSVC) @@ -82,10 +81,6 @@ if(MSVC) endif() if(WITH_OTLP) - set(WITH_PROTOBUF ON) -endif() - -if(WITH_PROTOBUF) set(protobuf_MODULE_COMPATIBLE ON) find_package(Protobuf) find_package(gRPC) @@ -93,6 +88,12 @@ if(WITH_PROTOBUF) if(WIN32 AND (NOT DEFINED CMAKE_TOOLCHAIN_FILE)) install_windows_deps() endif() + + if(NOT DEFINED CMAKE_TOOLCHAIN_FILE) + message(STATUS_FATAL "Windows dependency installation failed!") + endif() + include(${CMAKE_TOOLCHAIN_FILE}) + if(NOT protobuf_FOUND) find_package(Protobuf REQUIRED) endif()