Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[submodule "spec"]
path = spec
url = https://github.com/open-feature/spec.git
17 changes: 17 additions & 0 deletions openfeature/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ cc_library(
":provider_status",
":reason",
":resolution_details",
":value",
],
)

Expand Down Expand Up @@ -69,6 +70,7 @@ cc_library(
include_prefix = "openfeature",
deps = [
":evaluation_context",
":value"
],
)

Expand Down Expand Up @@ -121,6 +123,7 @@ cc_library(
":metadata",
":provider",
":resolution_details",
":value",
],
)

Expand Down Expand Up @@ -165,6 +168,7 @@ cc_library(
":evaluation_context",
":metadata",
":resolution_details",
":value",
],
)

Expand All @@ -179,6 +183,19 @@ cc_library(
srcs = ["resolution_details.cpp"],
hdrs = ["resolution_details.h"],
include_prefix = "openfeature",
deps = [
":error_code",
":flag_metadata",
":reason",
":value",
],
)

cc_library(
name = "value",
srcs = ["value.cpp"],
hdrs = ["value.h"],
include_prefix = "openfeature",
deps = [
":error_code",
":flag_metadata",
Expand Down
128 changes: 107 additions & 21 deletions openfeature/client_api.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,36 +38,122 @@ bool ClientAPI::GetBooleanValue(std::string_view flag_key, bool default_value,
return EvaluateBooleanFlag(flag_key, default_value, ctx)->GetValue();
}

std::string ClientAPI::GetStringValue(std::string_view flag_key,
std::string_view default_value) {
return EvaluateStringFlag(flag_key, default_value, std::nullopt)->GetValue();
}

std::string ClientAPI::GetStringValue(std::string_view flag_key,
std::string_view default_value,
const EvaluationContext& ctx) {
return EvaluateStringFlag(flag_key, default_value, ctx)->GetValue();
}

int64_t ClientAPI::GetIntegerValue(std::string_view flag_key,
int64_t default_value) {
return EvaluateIntegerFlag(flag_key, default_value, std::nullopt)->GetValue();
}

int64_t ClientAPI::GetIntegerValue(std::string_view flag_key,
int64_t default_value,
const EvaluationContext& ctx) {
return EvaluateIntegerFlag(flag_key, default_value, ctx)->GetValue();
}

double ClientAPI::GetDoubleValue(std::string_view flag_key,
double default_value) {
return EvaluateDoubleFlag(flag_key, default_value, std::nullopt)->GetValue();
}

double ClientAPI::GetDoubleValue(std::string_view flag_key,
double default_value,
const EvaluationContext& ctx) {
return EvaluateDoubleFlag(flag_key, default_value, ctx)->GetValue();
}

Value ClientAPI::GetObjectValue(std::string_view flag_key,
Value default_value) {
return EvaluateObjectFlag(flag_key, default_value, std::nullopt)->GetValue();
}

Value ClientAPI::GetObjectValue(std::string_view flag_key, Value default_value,
const EvaluationContext& ctx) {
return EvaluateObjectFlag(flag_key, default_value, ctx)->GetValue();
}

std::unique_ptr<BoolResolutionDetails> ClientAPI::EvaluateBooleanFlag(
std::string_view flag_key, bool default_value,
const std::optional<EvaluationContext>& ctx) {
if (GetProviderStatus() != ProviderStatus::kReady) {
return std::make_unique<BoolResolutionDetails>(
default_value, Reason::kError, std::nullopt, FlagMetadata(),
ErrorCode::kProviderNotReady, "Provider is not ready");
}
return this->EvaluateFlag<BoolResolutionDetails>(
default_value, ctx,
[&](const std::shared_ptr<FeatureProvider>& provider,
const EvaluationContext& merged_ctx) {
return provider->GetBooleanEvaluation(flag_key, default_value,
merged_ctx);
});
}

std::shared_ptr<FeatureProvider> provider =
provider_repository_.GetProvider(domain_);
if (!provider) {
return std::make_unique<BoolResolutionDetails>(
default_value, Reason::kError, std::nullopt, FlagMetadata(),
ErrorCode::kProviderFatal, "Provider not found for domain");
}
std::unique_ptr<StringResolutionDetails> ClientAPI::EvaluateStringFlag(
std::string_view flag_key, std::string_view default_value,
const std::optional<EvaluationContext>& ctx) {
std::string default_str(default_value);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

To avoid creating a temporary std::string from the std::string_view default_value, you can pass default_value directly to EvaluateFlag and construct the std::string inside the lambda. This is slightly more efficient as the string is only constructed if the provider call is actually made.

  return this->EvaluateFlag<StringResolutionDetails>(
      default_value, ctx,
      [&](const std::shared_ptr<FeatureProvider>& provider,
          const EvaluationContext& merged_ctx) {
        return provider->GetStringEvaluation(flag_key, default_value,
                                             merged_ctx);
      });

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I implement this change, the generic EvaluateFlag will try to create a ResolutionDetailsType with a std::string_view instead of a std::string as a default_value. This results in a type mismatch error. To make this work, inside EvaluateFlag, I need to check if ResolutionDetailsType is StringResolutionDetails and convert std::string_view to std::string. Do you think this is worth it for the optimization above? If yes, lmk and I'll do the necessary changes.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, I see the templated logic - you can leave it as is

return this->EvaluateFlag<StringResolutionDetails>(
default_str, ctx,
[&](const std::shared_ptr<FeatureProvider>& provider,
const EvaluationContext& merged_ctx) {
return provider->GetStringEvaluation(flag_key, default_value,
merged_ctx);
});
}

EvaluationContext merged_context = MergeContexts(ctx);
return provider->GetBooleanEvaluation(flag_key, default_value,
merged_context);
std::unique_ptr<IntResolutionDetails> ClientAPI::EvaluateIntegerFlag(
std::string_view flag_key, int64_t default_value,
const std::optional<EvaluationContext>& ctx) {
return this->EvaluateFlag<IntResolutionDetails>(
default_value, ctx,
[&](const std::shared_ptr<FeatureProvider>& provider,
const EvaluationContext& merged_ctx) {
return provider->GetIntegerEvaluation(flag_key, default_value,
merged_ctx);
});
}

std::unique_ptr<DoubleResolutionDetails> ClientAPI::EvaluateDoubleFlag(
std::string_view flag_key, double default_value,
const std::optional<EvaluationContext>& ctx) {
return this->EvaluateFlag<DoubleResolutionDetails>(
default_value, ctx,
[&](const std::shared_ptr<FeatureProvider>& provider,
const EvaluationContext& merged_ctx) {
return provider->GetDoubleEvaluation(flag_key, default_value,
merged_ctx);
});
}

std::unique_ptr<ObjectResolutionDetails> ClientAPI::EvaluateObjectFlag(
std::string_view flag_key, Value default_value,
const std::optional<EvaluationContext>& ctx) {
return this->EvaluateFlag<ObjectResolutionDetails>(
default_value, ctx,
[&](const std::shared_ptr<FeatureProvider>& provider,
const EvaluationContext& merged_ctx) {
return provider->GetObjectEvaluation(flag_key, default_value,
merged_ctx);
});
}

EvaluationContext ClientAPI::MergeContexts(
const std::optional<EvaluationContext>& invocation_ctx) {
// TODO: Add context merging logic after EvaluationContext is implemented.

if (invocation_ctx) {
return *invocation_ctx;
EvaluationContext global_ctx =
GlobalContextManager::GetInstance().GetGlobalEvaluationContext();
EvaluationContext client_ctx = GetEvaluationContext();

if (invocation_ctx.has_value()) {
return EvaluationContext::Merge(
{&global_ctx, &client_ctx, &(*invocation_ctx)});
} else {
return EvaluationContext::Merge({&global_ctx, &client_ctx});
}
return GetEvaluationContext();
}

} // namespace openfeature
} // namespace openfeature
71 changes: 69 additions & 2 deletions openfeature/client_api.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include "openfeature/provider_repository.h"
#include "openfeature/provider_status.h"
#include "openfeature/resolution_details.h"
#include "openfeature/value.h"

namespace openfeature {

Expand All @@ -40,21 +41,64 @@ class ClientAPI : public Client {
// Returns the current status of the associated provider.
ProviderStatus GetProviderStatus() override;

// Evaluate a boolean flag.
bool GetBooleanValue(std::string_view flag_key, bool default_value) override;
bool GetBooleanValue(std::string_view flag_key, bool default_value,
const EvaluationContext& ctx) override;

// Evaluate a string flag.
std::string GetStringValue(std::string_view flag_key,
std::string_view default_value) override;
std::string GetStringValue(std::string_view flag_key,
std::string_view default_value,
const EvaluationContext& ctx) override;

// Evaluate an integer flag.
int64_t GetIntegerValue(std::string_view flag_key,
int64_t default_value) override;
int64_t GetIntegerValue(std::string_view flag_key, int64_t default_value,
const EvaluationContext& ctx) override;
// Evaluate a double flag.
double GetDoubleValue(std::string_view flag_key,
double default_value) override;
double GetDoubleValue(std::string_view flag_key, double default_value,
const EvaluationContext& ctx) override;
// Evaluate an object flag.
Value GetObjectValue(std::string_view flag_key, Value default_value) override;
Value GetObjectValue(std::string_view flag_key, Value default_value,
const EvaluationContext& ctx) override;

// TODO: Add methods to get and set Hooks.
// TODO: Add methods for flag evaluation for other types (e.g. string, int,
// float, object).
// TODO: Add methods for detailed flag evaluation.
// TODO: Overload method "GetBooleanValue" to accept "Evaluation Options".

private:
template <typename ResolutionDetailsType, typename ValueType,
typename ProviderCallable>
std::unique_ptr<ResolutionDetailsType> EvaluateFlag(
ValueType default_value, const std::optional<EvaluationContext>& ctx,
ProviderCallable provider_call);

std::unique_ptr<BoolResolutionDetails> EvaluateBooleanFlag(
std::string_view flag_key, bool default_value,
const std::optional<EvaluationContext>& ctx);

std::unique_ptr<StringResolutionDetails> EvaluateStringFlag(
std::string_view flag_key, std::string_view default_value,
const std::optional<EvaluationContext>& ctx);

std::unique_ptr<IntResolutionDetails> EvaluateIntegerFlag(
std::string_view flag_key, int64_t default_value,
const std::optional<EvaluationContext>& ctx);

std::unique_ptr<DoubleResolutionDetails> EvaluateDoubleFlag(
std::string_view flag_key, double default_value,
const std::optional<EvaluationContext>& ctx);

std::unique_ptr<ObjectResolutionDetails> EvaluateObjectFlag(
std::string_view flag_key, Value default_value,
const std::optional<EvaluationContext>& ctx);

EvaluationContext MergeContexts(
const std::optional<EvaluationContext>& invocation_ctx);

Expand All @@ -64,6 +108,29 @@ class ClientAPI : public Client {
mutable std::mutex context_mutex_;
};

template <typename ResolutionDetailsType, typename ValueType,
typename ProviderCallable>
std::unique_ptr<ResolutionDetailsType> ClientAPI::EvaluateFlag(
ValueType default_value, const std::optional<EvaluationContext>& ctx,
ProviderCallable provider_call) {
if (GetProviderStatus() != ProviderStatus::kReady) {
return std::make_unique<ResolutionDetailsType>(
default_value, Reason::kError, std::nullopt, FlagMetadata(),
ErrorCode::kProviderNotReady, "Provider is not ready");
}

std::shared_ptr<FeatureProvider> provider =
provider_repository_.GetProvider(domain_);
if (!provider) {
return std::make_unique<ResolutionDetailsType>(
default_value, Reason::kError, std::nullopt, FlagMetadata(),
ErrorCode::kProviderFatal, "Provider not found for domain");
}

EvaluationContext merged_context = MergeContexts(ctx);
return provider_call(provider, merged_context);
}

} // namespace openfeature

#endif // CPP_SDK_INCLUDE_OPENFEATURE_CLIENT_API_H_
25 changes: 24 additions & 1 deletion openfeature/features.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,41 @@
#include <string_view>

#include "openfeature/evaluation_context.h"
#include "openfeature/value.h"

namespace openfeature {

class Features {
public:
virtual ~Features() = default;

virtual bool GetBooleanValue(std::string_view flag_key,
bool default_value) = 0;
virtual bool GetBooleanValue(std::string_view flag_key, bool default_value,
const EvaluationContext& ctx) = 0;

// TODO: Add other flag types (e.g. string, int, float, object)
virtual std::string GetStringValue(std::string_view flag_key,
std::string_view default_value) = 0;
virtual std::string GetStringValue(std::string_view flag_key,
std::string_view default_value,
const EvaluationContext& ctx) = 0;

virtual int64_t GetIntegerValue(std::string_view flag_key,
int64_t default_value) = 0;
virtual int64_t GetIntegerValue(std::string_view flag_key,
int64_t default_value,
const EvaluationContext& ctx) = 0;

virtual double GetDoubleValue(std::string_view flag_key,
double default_value) = 0;
virtual double GetDoubleValue(std::string_view flag_key, double default_value,
const EvaluationContext& ctx) = 0;

virtual Value GetObjectValue(std::string_view flag_key,
Value default_value) = 0;
virtual Value GetObjectValue(std::string_view flag_key, Value default_value,
const EvaluationContext& ctx) = 0;

// TODO: Add detailed evaluation methods
};

Expand Down
30 changes: 30 additions & 0 deletions openfeature/noop_provider.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,34 @@ std::unique_ptr<BoolResolutionDetails> NoopProvider::GetBooleanEvaluation(
std::nullopt, "");
}

std::unique_ptr<StringResolutionDetails> NoopProvider::GetStringEvaluation(
std::string_view flag, std::string_view default_value,
const EvaluationContext& ctx) {
return std::make_unique<StringResolutionDetails>(
std::string(default_value), Reason::kDefault, "default-variant",
FlagMetadata(), std::nullopt, "");
}

std::unique_ptr<IntResolutionDetails> NoopProvider::GetIntegerEvaluation(
std::string_view flag, int64_t default_value,
const EvaluationContext& ctx) {
return std::make_unique<IntResolutionDetails>(
default_value, Reason::kDefault, "default-variant", FlagMetadata(),
std::nullopt, "");
}

std::unique_ptr<DoubleResolutionDetails> NoopProvider::GetDoubleEvaluation(
std::string_view flag, double default_value, const EvaluationContext& ctx) {
return std::make_unique<DoubleResolutionDetails>(
default_value, Reason::kDefault, "default-variant", FlagMetadata(),
std::nullopt, "");
}

std::unique_ptr<ObjectResolutionDetails> NoopProvider::GetObjectEvaluation(
std::string_view flag, Value default_value, const EvaluationContext& ctx) {
return std::make_unique<ObjectResolutionDetails>(
default_value, Reason::kDefault, "default-variant", FlagMetadata(),
std::nullopt, "");
}

} // namespace openfeature
Loading