Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
42a776d
base data structures fot hook implementation.
NeaguGeorgiana23 Jul 7, 2026
e968609
fix linter
NeaguGeorgiana23 Jul 7, 2026
334ec2a
fix linter
NeaguGeorgiana23 Jul 7, 2026
f110e5c
Merge branch 'main' into define-hook-data-structures
NeaguGeorgiana23 Jul 7, 2026
5cc878b
Update BUILD file.
NeaguGeorgiana23 Jul 10, 2026
bd66430
Correct include guards.
NeaguGeorgiana23 Jul 10, 2026
5332610
feat: Add HookData class.
NeaguGeorgiana23 Jul 10, 2026
c8fc92d
fix linter
NeaguGeorgiana23 Jul 10, 2026
3f5f4de
fix linter
NeaguGeorgiana23 Jul 10, 2026
b0f3a15
fix linter
NeaguGeorgiana23 Jul 10, 2026
e9e8852
fix linter
NeaguGeorgiana23 Jul 10, 2026
e333df0
add HookContext structure.
NeaguGeorgiana23 Jul 13, 2026
c59de28
fix linter
NeaguGeorgiana23 Jul 13, 2026
45ca64a
fix linter
NeaguGeorgiana23 Jul 13, 2026
3b93d77
add Hooks class.
NeaguGeorgiana23 Jul 14, 2026
a4aa1c2
fix linter.
NeaguGeorgiana23 Jul 14, 2026
d7cd54a
fix linter.
NeaguGeorgiana23 Jul 14, 2026
d6ef2c6
fix linter.
NeaguGeorgiana23 Jul 14, 2026
a2f7fee
fix linter.
NeaguGeorgiana23 Jul 14, 2026
a91afca
appli agent suggestions.
NeaguGeorgiana23 Jul 14, 2026
c0482f7
Merge branch 'main' into hooks
NeaguGeorgiana23 Jul 14, 2026
a0ca155
Merge branch 'main' into hooks
NeaguGeorgiana23 Jul 14, 2026
06bd51c
Add EvaluationOpton struct.
NeaguGeorgiana23 Jul 15, 2026
b362223
fix linter.
NeaguGeorgiana23 Jul 15, 2026
6942c4e
update Provider class to allow getting hooks.
NeaguGeorgiana23 Jul 15, 2026
49bdc17
update all classes that inharit from FeatureProvider.
NeaguGeorgiana23 Jul 15, 2026
dab62a0
update BUILD files and imports.
NeaguGeorgiana23 Jul 15, 2026
ea6a463
update GlobalAPI with hook methods.
NeaguGeorgiana23 Jul 15, 2026
665914a
fix linter.
NeaguGeorgiana23 Jul 16, 2026
0bd0f58
fix linter.
NeaguGeorgiana23 Jul 16, 2026
153fc57
Merge branch 'main' of https://github.com/open-feature/cpp-sdk into u…
NeaguGeorgiana23 Aug 10, 2026
543edd0
Fix linter.
NeaguGeorgiana23 Aug 10, 2026
3e28c56
Update Clint API to support Hooks
NeaguGeorgiana23 Aug 10, 2026
83ee12c
fix linter.
NeaguGeorgiana23 Aug 10, 2026
098cf4a
Merge branch 'main' into hook_support_for_client_class
NeaguGeorgiana23 Aug 11, 2026
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
2 changes: 2 additions & 0 deletions openfeature/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ cc_library(
deps = [
":evaluation_context",
":features",
":general_hook",
":metadata",
":provider_status",
],
Expand All @@ -64,6 +65,7 @@ cc_library(
":evaluation_context",
":features",
":flag_metadata",
":general_hook",
":global_context_manager",
":metadata",
":provider",
Expand Down
15 changes: 13 additions & 2 deletions openfeature/client.h
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
#ifndef CPP_SDK_INCLUDE_OPENFEATURE_CLIENT_H_
#define CPP_SDK_INCLUDE_OPENFEATURE_CLIENT_H_

#include <memory>
#include <vector>

#include "openfeature/evaluation_context.h"
#include "openfeature/features.h"
#include "openfeature/general_hook.h"
#include "openfeature/metadata.h"
#include "openfeature/provider_status.h"

Expand All @@ -11,7 +15,7 @@ namespace openfeature {
// OpenFeature client implementation.
class Client : public Features {
public:
virtual ~Client() = default;
~Client() override = default;
virtual Metadata GetMetadata() = 0;

// Return an optional client-level evaluation context.
Expand All @@ -23,7 +27,14 @@ class Client : public Features {
// Returns the current status of the associated provider.
virtual ProviderStatus GetProviderStatus() = 0;

// TODO: Add methods to add and get Hooks
// Adds one or more hooks to the client-level hook repository.
virtual void AddHooks(std::vector<std::shared_ptr<GeneralHook>> hooks) = 0;

// Adds a single hook to the client-level hook repository.
virtual void AddHook(std::shared_ptr<GeneralHook> hook) = 0;

// Retrieves all configured client-level hooks.
virtual std::vector<std::shared_ptr<GeneralHook>> GetHooks() const = 0;
};

} // namespace openfeature
Expand Down
28 changes: 24 additions & 4 deletions openfeature/client_api.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,12 @@ ClientAPI::ClientAPI(ProviderRepository& repository, std::string_view domain)
Metadata ClientAPI::GetMetadata() { return Metadata{domain_}; }

EvaluationContext ClientAPI::GetEvaluationContext() {
std::lock_guard<std::mutex> lock(context_mutex_);
std::scoped_lock<std::mutex> lock(context_mutex_);
return evaluation_context_;
}

void ClientAPI::SetEvaluationContext(const EvaluationContext& ctx) {
std::lock_guard<std::mutex> lock(context_mutex_);
std::scoped_lock<std::mutex> lock(context_mutex_);
evaluation_context_ = ctx;
}

Expand Down Expand Up @@ -151,9 +151,29 @@ EvaluationContext ClientAPI::MergeContexts(
if (invocation_ctx.has_value()) {
return EvaluationContext::Merge(
{&global_ctx, &client_ctx, &(*invocation_ctx)});
} else {
return EvaluationContext::Merge({&global_ctx, &client_ctx});
}
return EvaluationContext::Merge({&global_ctx, &client_ctx});
}

void ClientAPI::AddHooks(std::vector<std::shared_ptr<GeneralHook>> hooks) {
std::unique_lock lock(hooks_mutex_);
hooks_.reserve(hooks_.size() + hooks.size());
for (auto& hook : hooks) {
if (hook != nullptr) {
hooks_.push_back(std::move(hook));
}
}
}

void ClientAPI::AddHook(std::shared_ptr<GeneralHook> hook) {
if (hook == nullptr) return;
std::unique_lock lock(hooks_mutex_);
hooks_.push_back(std::move(hook));
}

std::vector<std::shared_ptr<GeneralHook>> ClientAPI::GetHooks() const {
std::shared_lock lock(hooks_mutex_);
return hooks_;
}

} // namespace openfeature
14 changes: 13 additions & 1 deletion openfeature/client_api.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@
#include <optional>
#include <string>
#include <string_view>
#include <vector>

#include "openfeature/client.h"
#include "openfeature/evaluation_context.h"
#include "openfeature/features.h"
#include "openfeature/general_hook.h"
Comment on lines +9 to +14

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

rg -n '^[[:space:]]*`#include` <(mutex|shared_mutex)>|std::shared_(mutex|lock)' \
  openfeature/client_api.h

Repository: open-feature/cpp-sdk

Length of output: 223


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

printf '%s\n' '--- openfeature/client_api.h ---'
sed -n '1,180p' openfeature/client_api.h

printf '%s\n' '--- shared synchronization symbols in the repository ---'
rg -n 'std::shared_(mutex|lock)|shared_mutex|shared_lock' --glob '*.{h,hpp,cc,cpp,cxx}' .

Repository: open-feature/cpp-sdk

Length of output: 9928


Include <shared_mutex> directly.

openfeature/client_api.h declares std::shared_mutex but includes only <mutex>. Add <shared_mutex> to prevent reliance on transitive includes.

Proposed fix
 `#include` <optional>
+#include <shared_mutex>
 `#include` <string>
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@openfeature/client_api.h` around lines 9 - 14, Update
openfeature/client_api.h to include the standard <shared_mutex> header directly,
alongside the existing standard-library includes, so its std::shared_mutex
declaration does not rely on transitive inclusion.

#include "openfeature/global_context_manager.h"
#include "openfeature/metadata.h"
#include "openfeature/provider.h"
Expand Down Expand Up @@ -68,7 +70,15 @@ class ClientAPI : public Client {
Value GetObjectValue(std::string_view flag_key, Value default_value,
const EvaluationContext& ctx) override;

// TODO: Add methods to get and set Hooks.
// Adds one or more hooks to the client-level hook repository.
void AddHooks(std::vector<std::shared_ptr<GeneralHook>> hooks) override;

// Adds a single hook to the client-level hook repository.
void AddHook(std::shared_ptr<GeneralHook> hook) override;

// Retrieves all configured client-level hooks.
std::vector<std::shared_ptr<GeneralHook>> GetHooks() const override;

// TODO: Add methods for detailed flag evaluation.
// TODO: Overload method "GetBooleanValue" to accept "Evaluation Options".

Expand Down Expand Up @@ -106,6 +116,8 @@ class ClientAPI : public Client {
std::string domain_;
EvaluationContext evaluation_context_;
mutable std::mutex context_mutex_;
mutable std::shared_mutex hooks_mutex_;
std::vector<std::shared_ptr<GeneralHook>> hooks_;
};

template <typename ResolutionDetailsType, typename ValueType,
Expand Down
1 change: 1 addition & 0 deletions openfeature/openfeature_api.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include <memory>
#include <shared_mutex>
#include <string_view>
#pragma endregion

#include "openfeature/client.h"
#include "openfeature/evaluation_context.h"
Expand Down
1 change: 1 addition & 0 deletions test/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ cc_test(
deps = [
":mock_feature_provider",
"//openfeature:client_api",
"//openfeature:hook",
"@googletest//:gtest_main",
],
)
Expand Down
66 changes: 66 additions & 0 deletions test/client_api_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include "mocks/mock_feature_provider.h"
#include "openfeature/evaluation_context.h"
#include "openfeature/global_context_manager.h"
#include "openfeature/hook.h"
#include "openfeature/provider_status.h"

using ::openfeature::BoolResolutionDetails;
Expand Down Expand Up @@ -440,3 +441,68 @@ TEST_F(ClientAPITest, ParallelProviderSwapRaceCondition) {
evaluation_thread.join();
proceed_init->set_value();
}

namespace {
class DummyHook1 : public openfeature::BoolHook {};
class DummyHook2 : public openfeature::StringHook {};
} // namespace

// Test that client is initialized with empty hooks by default.
TEST_F(ClientAPITest, InitialStateHasEmptyHooks) {
ClientAPI client(repo_, "test-domain");
EXPECT_TRUE(client.GetHooks().empty());
}

// Test adding a single hook via AddHook.
TEST_F(ClientAPITest, AddHookAppendsSingleHook) {
ClientAPI client(repo_, "test-domain");
std::shared_ptr<openfeature::GeneralHook> hook1 =
std::make_shared<DummyHook1>();
client.AddHook(hook1);

auto hooks = client.GetHooks();
ASSERT_EQ(hooks.size(), 1);
EXPECT_EQ(hooks[0], hook1);
}

// Test adding multiple hooks via AddHooks and preserving registration order.
TEST_F(ClientAPITest, AddHooksAppendsMultipleHooksAndPreservesOrder) {
ClientAPI client(repo_, "test-domain");
std::shared_ptr<openfeature::GeneralHook> hook1 =
std::make_shared<DummyHook1>();
std::shared_ptr<openfeature::GeneralHook> hook2 =
std::make_shared<DummyHook2>();

client.AddHooks({hook1, hook2});

auto hooks = client.GetHooks();
ASSERT_EQ(hooks.size(), 2);
EXPECT_EQ(hooks[0], hook1);
EXPECT_EQ(hooks[1], hook2);

// Adding another hook appends without clearing existing ones
std::shared_ptr<openfeature::GeneralHook> hook3 =
std::make_shared<DummyHook1>();
client.AddHook(hook3);

hooks = client.GetHooks();
ASSERT_EQ(hooks.size(), 3);
EXPECT_EQ(hooks[0], hook1);
EXPECT_EQ(hooks[1], hook2);
EXPECT_EQ(hooks[2], hook3);
}

// Test that AddHook and AddHooks filter out nullptr entries.
TEST_F(ClientAPITest, AddHookAndAddHooksFiltersNullptrs) {
ClientAPI client(repo_, "test-domain");
client.AddHook(nullptr);
EXPECT_TRUE(client.GetHooks().empty());

std::shared_ptr<openfeature::GeneralHook> valid_hook =
std::make_shared<DummyHook1>();
client.AddHooks({nullptr, valid_hook, nullptr});

auto hooks = client.GetHooks();
ASSERT_EQ(hooks.size(), 1);
EXPECT_EQ(hooks[0], valid_hook);
}