diff --git a/.gitignore b/.gitignore index e69de29..d163863 100644 --- a/.gitignore +++ b/.gitignore @@ -0,0 +1 @@ +build/ \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index 4531e4f..a6228a6 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -8,6 +8,56 @@ "*.tcc": "cpp", "deque": "cpp", "unordered_map": "cpp", - "system_error": "cpp" + "system_error": "cpp", + "atomic": "cpp", + "shared_mutex": "cpp", + "mutex": "cpp", + "cctype": "cpp", + "clocale": "cpp", + "cmath": "cpp", + "cstdarg": "cpp", + "cstddef": "cpp", + "cstdio": "cpp", + "cstdlib": "cpp", + "ctime": "cpp", + "cwchar": "cpp", + "cwctype": "cpp", + "array": "cpp", + "bit": "cpp", + "chrono": "cpp", + "compare": "cpp", + "concepts": "cpp", + "condition_variable": "cpp", + "cstdint": "cpp", + "exception": "cpp", + "algorithm": "cpp", + "functional": "cpp", + "iterator": "cpp", + "memory": "cpp", + "memory_resource": "cpp", + "numeric": "cpp", + "optional": "cpp", + "random": "cpp", + "ratio": "cpp", + "string_view": "cpp", + "tuple": "cpp", + "type_traits": "cpp", + "utility": "cpp", + "fstream": "cpp", + "initializer_list": "cpp", + "iosfwd": "cpp", + "istream": "cpp", + "limits": "cpp", + "new": "cpp", + "numbers": "cpp", + "semaphore": "cpp", + "sstream": "cpp", + "stdexcept": "cpp", + "stop_token": "cpp", + "streambuf": "cpp", + "thread": "cpp", + "cinttypes": "cpp", + "typeinfo": "cpp", + "variant": "cpp" } } \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..cd21d5c --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,30 @@ +cmake_minimum_required(VERSION 3.10) + +project(metrics) + +# Postavi verziju projekta +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +# Pronađi Boost biblioteku (ako nije uobičajeni direktorijum, postavi put) +# find_package(Boost REQUIRED COMPONENTS system) + +# Dodaj direktorijume sa zaglavljima +include_directories(lib) + +# Dodaj biblioteku +add_library(metrics STATIC + src/metrics.cpp +) + +# # Linkaj log biblioteku sa Boost-om +# target_link_libraries(log Boost::system) + +# Dodaj testove +add_subdirectory(test) + + +# Instaliraj biblioteku +# install(TARGETS log DESTINATION lib) +# install(FILES lib/log.hpp lib/define.hpp lib/engine.hpp lib/filesystem.hpp lib/timers.hpp lib/trigger.hpp DESTINATION include/log) +# \ No newline at end of file diff --git a/README.md b/README.md index dcf224d..c0b97e9 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ using namespace marcelb; ```c++ // init -Metrics stats; +Metrics stats; // operator [] and increment ++ stats["access"]++; // print couter access @@ -39,7 +39,7 @@ auto data = stats.get_data(); // reset counters stats.clear(); // set counters -map MyStats = {{"access", 3},{ "error", 0}}; +map MyStats = {{"access", 3},{ "error", 0}}; stats.set(MyStats); // get and clear all counters auto dataWithClear = stats.get_data_and_clear(); diff --git a/lib/metrics.hpp b/lib/metrics.hpp index f75130c..c9fe533 100644 --- a/lib/metrics.hpp +++ b/lib/metrics.hpp @@ -6,77 +6,69 @@ #include #include #include +#include +#include using namespace std; namespace marcelb { /** - * A class for measuring arbitrary statistics -*/ + * A template class for measuring arbitrary statistics + */ +template class Metrics { - mutex io; - map counters; - - public: + mutable shared_mutex mtx; // Mutex for concurrent read, exclusive write + map> counters; // Supports any K (e.g., string, int) +public: /** * Constructor, without predefining the name of the counter - */ - Metrics (); + */ + Metrics(); /** * Constructor, with predefined counters from the passed object - */ - Metrics (map _counters); + */ + Metrics(map _counters); /** * Operator[] to access each measurement counter - */ - uint& operator[](const string& key) { - return counters[key]; - } + */ + atomic& operator[](const K& key); /** * Operator++ to increment the measurement counter incrementally - */ - Metrics& operator++ (int n) { - lock_guard _io(io); - n++; - return *this; - } + */ + Metrics& operator++(int n); /** * Method to set the counter from the passed object - */ - void set(map _counters) { - counters = _counters; - } + */ + void set(map _counters); /** * A method to reset the counter - */ + */ void clear(); /** - * A method that returns a vector os strings of all counter names - */ - vector keys(); + * A method that returns a vector of strings of all counter names + */ + vector keys() const; /** - * The method returns a map of all measurements - */ - map get_data(); + * The method returns a map of all measurements + */ + map get_data() const; /** - * The method returns a map of all measurements and resets the counters - */ - map get_data_and_clear(); - + * The method returns a map of all measurements and resets the counters + */ + map get_data_and_clear(); }; - } #endif \ No newline at end of file diff --git a/src/metrics.cpp b/src/metrics.cpp index b48d149..9a30874 100644 --- a/src/metrics.cpp +++ b/src/metrics.cpp @@ -1,35 +1,78 @@ #include "../lib/metrics.hpp" -marcelb::Metrics::Metrics() { -} +namespace marcelb { -marcelb::Metrics::Metrics(map _counters) { +template +Metrics::Metrics() {} + +template +Metrics::Metrics(map _counters) { set(_counters); } -void marcelb::Metrics::clear() { - lock_guard _io(io); +template +void Metrics::clear() { + unique_lock lock(mtx); counters.clear(); - return; } -vector marcelb::Metrics::keys() { - vector _keys; - for (auto counter : counters) { +template +vector Metrics::keys() const { + shared_lock lock(mtx); + vector _keys; + for (auto& counter : counters) { _keys.push_back(counter.first); } - return _keys; } - -map marcelb::Metrics::get_data() { - return counters; +template +map Metrics::get_data() const { + shared_lock lock(mtx); + map data; + for (const auto& counter : counters) { + data[counter.first] = counter.second.load(); + } + return data; } - -map marcelb::Metrics::get_data_and_clear() { - auto data = counters; +template +map Metrics::get_data_and_clear() { + map data; + { + shared_lock lock(mtx); + auto data = get_data(); + } clear(); return data; +} + +template +atomic& Metrics::operator[](const K& key) { + shared_lock lock(mtx); + return counters[key]; +} + +template +Metrics& Metrics::operator++(int n) { + shared_lock lock(mtx); + // Increment all counters by n as an example + for (auto& counter : counters) { + counter.second.fetch_add(n, memory_order_relaxed); + } + return *this; +} + +template +void Metrics::set(map _counters) { + unique_lock lock(mtx); + counters.clear(); + for (auto& counter : _counters) { + counters[counter.first].store(counter.second); + } +} + +template class Metrics; +template class Metrics; + } \ No newline at end of file diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt new file mode 100644 index 0000000..a8e77d2 --- /dev/null +++ b/test/CMakeLists.txt @@ -0,0 +1,3 @@ +add_executable(metrics_test test.cpp) + +target_link_libraries(metrics_test metrics) \ No newline at end of file diff --git a/test/test b/test/test deleted file mode 100755 index db2161a..0000000 Binary files a/test/test and /dev/null differ diff --git a/test/test.cpp b/test/test.cpp index 19ce70b..3af2468 100644 --- a/test/test.cpp +++ b/test/test.cpp @@ -1,21 +1,54 @@ +#include +#include +#include + +using namespace std; + #include "../lib/metrics.hpp" using namespace marcelb; int main() { - Metrics metrika; + // atomic a(0); + + // a++; + + // cout << a << endl; + + Metrics metrika; cout << " access " << metrika["access"]++ << endl; cout << " access " << metrika["access"]++ << endl; cout << " error " << metrika["error"] << endl; cout << " access " << metrika["access"] << endl; - metrika.keys(); + // metrika.keys(); cout << " error " << metrika["error"] << endl; cout << " access " << metrika["access"] << endl; + Metrics metrika2; + + + enum keys { + access = 1, + error + }; + + + + cout << " access " << metrika2[keys::access]++ << endl; + cout << " access " << metrika2[keys::access]++ << endl; + cout << " error " << metrika2[keys::error] << endl; + cout << " access " << metrika2[keys::access] << endl; + + // metrika2.keys(); + + cout << " error " << metrika2[keys::error] << endl; + cout << " access " << metrika2[keys::access] << endl; + + // // init // Metrics stats; // // operator [] and increment ++ @@ -29,7 +62,7 @@ int main() { // // reset counters // stats.clear(); // // set counters - // map MyStats = {{"access", 3},{ "error", 0}}; + // map MyStats = {{"access", 3},{ "error", 0}}; // stats.set(MyStats); // // get and clear all counters // auto dataWithClear = stats.get_data_and_clear();