Skip to content

Commit c8b9954

Browse files
feat: Add implementation for type safe configuration library
1 parent 673209e commit c8b9954

15 files changed

Lines changed: 930 additions & 437 deletions

CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,15 @@ project(cppfig VERSION 0.1.0)
44
set(CMAKE_CXX_STANDARD 17)
55

66
find_package(GTest CONFIG REQUIRED)
7+
find_package(nlohmann_json CONFIG REQUIRED)
78

89
option(STRICT_BUILD "Enable warnings as errors" ON)
910

1011
configure_file(CMakeConfig.h.in "${CMAKE_BINARY_DIR}/CMakeConfig.h")
1112
include_directories(${CMAKE_BINARY_DIR})
1213

1314
add_subdirectory(src)
15+
add_subdirectory(examples)
1416

1517
add_subdirectory(test)
1618
include(CTest)

examples/CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
cmake_minimum_required(VERSION 3.22.0)
2+
3+
add_executable(cppfig-example example.cpp)
4+
target_link_libraries(cppfig-example PRIVATE cppfig)

examples/ExampleConfiguration.h

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
#pragma once
2+
3+
#include <cstdint>
4+
#include <filesystem>
5+
#include <optional>
6+
#include <stdexcept>
7+
#include <string>
8+
#include <variant>
9+
10+
#include "GenericConfiguration.h"
11+
#include "JsonSerializer.h"
12+
#include "Setting.h"
13+
14+
namespace example {
15+
16+
// Define our configuration enum
17+
enum class AppConfigName : uint8_t {
18+
DatabaseUrl,
19+
MaxConnections,
20+
EnableLogging,
21+
RetryCount,
22+
LogLevel
23+
};
24+
25+
// Implement the toString and fromString functions for the enum
26+
// (These are required by JsonSerializer)
27+
inline std::string ToString(AppConfigName name)
28+
{
29+
switch (name) {
30+
case AppConfigName::DatabaseUrl:
31+
return "database_url";
32+
case AppConfigName::MaxConnections:
33+
return "max_connections";
34+
case AppConfigName::EnableLogging:
35+
return "enable_logging";
36+
case AppConfigName::RetryCount:
37+
return "retry_count";
38+
case AppConfigName::LogLevel:
39+
return "log_level";
40+
default:
41+
return "unknown";
42+
}
43+
}
44+
45+
inline AppConfigName FromString(const std::string& str)
46+
{
47+
if (str == "database_url") {
48+
return AppConfigName::DatabaseUrl;
49+
}
50+
if (str == "max_connections") {
51+
return AppConfigName::MaxConnections;
52+
}
53+
if (str == "enable_logging") {
54+
return AppConfigName::EnableLogging;
55+
}
56+
if (str == "retry_count") {
57+
return AppConfigName::RetryCount;
58+
}
59+
if (str == "log_level") {
60+
return AppConfigName::LogLevel;
61+
}
62+
throw std::runtime_error("Invalid configuration name: " + str);
63+
}
64+
65+
} // namespace example
66+
67+
// Provide template specializations for the JsonSerializer
68+
namespace config {
69+
template <>
70+
inline std::string JsonSerializer<example::AppConfigName>::ToString(example::AppConfigName enumValue)
71+
{
72+
return example::ToString(enumValue);
73+
}
74+
75+
template <>
76+
inline example::AppConfigName JsonSerializer<example::AppConfigName>::FromString(const std::string& str)
77+
{
78+
return example::FromString(str);
79+
}
80+
} // namespace config
81+
82+
namespace example {
83+
84+
// Define the configuration type
85+
using AppConfig = config::GenericConfiguration<AppConfigName, config::JsonSerializer<AppConfigName>>;
86+
87+
// Define the variant type for AppConfig settings
88+
using AppSettingVariant = std::variant<
89+
config::Setting<AppConfigName, int>,
90+
config::Setting<AppConfigName, float>,
91+
config::Setting<AppConfigName, double>,
92+
config::Setting<AppConfigName, std::string>,
93+
config::Setting<AppConfigName, bool>>;
94+
95+
// Define default configuration values
96+
inline const AppConfig::DefaultConfigMap DefaultAppConfig = {
97+
{ AppConfigName::DatabaseUrl,
98+
config::Setting<AppConfigName, std::string>(
99+
AppConfigName::DatabaseUrl,
100+
"mongodb://localhost:27017",
101+
std::nullopt,
102+
std::nullopt,
103+
std::nullopt,
104+
"URL for database connection") },
105+
{ AppConfigName::MaxConnections,
106+
config::Setting<AppConfigName, int>(
107+
AppConfigName::MaxConnections,
108+
100,
109+
1000,
110+
1,
111+
"connections",
112+
"Maximum number of database connections") },
113+
{ AppConfigName::EnableLogging,
114+
config::Setting<AppConfigName, bool>(
115+
AppConfigName::EnableLogging,
116+
true,
117+
std::nullopt,
118+
std::nullopt,
119+
std::nullopt,
120+
"Enable application logging") },
121+
{ AppConfigName::RetryCount,
122+
config::Setting<AppConfigName, int>(
123+
AppConfigName::RetryCount,
124+
3,
125+
10,
126+
0,
127+
"retries",
128+
"Number of retry attempts for operations") },
129+
{ AppConfigName::LogLevel,
130+
config::Setting<AppConfigName, std::string>(
131+
AppConfigName::LogLevel,
132+
"info",
133+
std::nullopt,
134+
std::nullopt,
135+
std::nullopt,
136+
"Logging level (debug, info, warning, error)") }
137+
};
138+
139+
// Example of how to use the configuration
140+
class Application {
141+
public:
142+
explicit Application(const std::filesystem::path& config_path)
143+
: config_(config_path, DefaultAppConfig)
144+
{
145+
// Access configuration values in a type-safe way with the new API
146+
auto db_url = config_.GetSetting(AppConfigName::DatabaseUrl).Value<std::string>();
147+
auto max_conn = config_.GetSetting(AppConfigName::MaxConnections).Value<int>();
148+
auto logging_enabled = config_.GetSetting(AppConfigName::EnableLogging).Value<bool>();
149+
150+
// Example of updating a setting
151+
config_.UpdateSetting(AppConfigName::RetryCount, 5);
152+
config_.Save();
153+
}
154+
155+
// Get the underlying config object
156+
AppConfig& GetConfig() { return config_; }
157+
158+
private:
159+
AppConfig config_;
160+
};
161+
162+
} // namespace example

examples/example.cpp

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
#include <filesystem>
2+
#include <iostream>
3+
4+
#include "ExampleConfiguration.h"
5+
6+
int main()
7+
{
8+
try {
9+
std::cout << "Configuration Library Example" << '\n';
10+
std::cout << "----------------------------" << '\n';
11+
12+
// Initialize the configuration with a file path
13+
std::filesystem::path config_path = "config.json";
14+
example::Application app(config_path);
15+
16+
// Access the configuration object
17+
auto& config = app.GetConfig();
18+
19+
// Demonstrate retrieving values with the new, more natural syntax
20+
std::cout << "Database URL: "
21+
<< config.GetSetting(example::AppConfigName::DatabaseUrl).Value<std::string>()
22+
<< '\n';
23+
24+
std::cout << "Max Connections: "
25+
<< config.GetSetting(example::AppConfigName::MaxConnections).Value<int>()
26+
<< '\n';
27+
28+
std::cout << "Logging Enabled: "
29+
<< (config.GetSetting(example::AppConfigName::EnableLogging).Value<bool>() ? "Yes" : "No")
30+
<< '\n';
31+
32+
std::cout << "Retry Count: "
33+
<< config.GetSetting(example::AppConfigName::RetryCount).Value<int>()
34+
<< '\n';
35+
36+
std::cout << "Log Level: "
37+
<< config.GetSetting(example::AppConfigName::LogLevel).Value<std::string>()
38+
<< '\n';
39+
40+
// Demonstrate updating a setting
41+
std::cout << "\nUpdating max connections to 200..." << '\n';
42+
config.UpdateSetting(example::AppConfigName::MaxConnections, 200);
43+
44+
std::cout << "New Max Connections: "
45+
<< config.GetSetting(example::AppConfigName::MaxConnections).Value<int>()
46+
<< '\n';
47+
48+
// Save changes
49+
if (config.Save()) {
50+
std::cout << "\nChanges saved successfully to \"" << config_path.string() << "\"" << '\n';
51+
}
52+
else {
53+
std::cout << "\nFailed to save changes" << '\n';
54+
}
55+
56+
return 0;
57+
}
58+
catch (const std::exception& e) {
59+
std::cerr << "Error: " << e.what() << '\n';
60+
return 1;
61+
}
62+
}

src/CMakeLists.txt

Lines changed: 8 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,20 @@
1-
add_library(cppfig)
1+
add_library(cppfig INTERFACE)
22

33
target_sources(cppfig
4-
PRIVATE
5-
${CMAKE_CURRENT_LIST_DIR}/Configuration.cpp
6-
PUBLIC
7-
${CMAKE_CURRENT_LIST_DIR}/Configuration.h
4+
INTERFACE
5+
${CMAKE_CURRENT_LIST_DIR}/GenericConfiguration.h
86
${CMAKE_CURRENT_LIST_DIR}/IConfiguration.h
7+
${CMAKE_CURRENT_LIST_DIR}/IConfigurationSerializer.h
8+
${CMAKE_CURRENT_LIST_DIR}/JsonSerializer.h
9+
${CMAKE_CURRENT_LIST_DIR}/Setting.h
910
)
1011

1112
target_include_directories(cppfig
12-
PUBLIC
13+
INTERFACE
1314
${CMAKE_CURRENT_LIST_DIR}
1415
)
1516

1617
target_link_libraries(cppfig
17-
PUBLIC
18+
INTERFACE
1819
nlohmann_json::nlohmann_json
1920
)
20-
21-
if(STRICT_BUILD)
22-
if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
23-
target_compile_options(cppfig PRIVATE -Wall -Wextra -Wpedantic -Werror -Wdocumentation)
24-
else()
25-
target_compile_options(cppfig PRIVATE -Wall -Wextra -Wpedantic -Werror)
26-
endif(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
27-
endif(STRICT_BUILD)

0 commit comments

Comments
 (0)