common_func.h

namespace sunny::common_func{
static bool trim(std::string& str_value) {
    if (!str_value.empty()) {
        str_value.erase(0, str_value.find_first_not_of(" \n\r\t"));
        str_value.erase(str_value.find_last_not_of(" \n\r\t") + 1);
        return true;
    }

    return false;
}
}

config.h

#pragma once

#include <fstream>
#include <iostream>
#include <map>

#include "common_func.h"

namespace sunny {
class config final {
public:
    static config& instance();

    config(const config&) = delete;

    config& operator=(const config&) = delete;

private:
    config() = default;

    ~config() = default;

public:
    bool start(std::string_view file_name);

    template <typename T>
    T read(const std::string& field_name) {
        auto value = configs_.find(field_name.data());
        if (value == configs_.end()) {
            std::cout << "config::read not found field_name: " << field_name << std::endl;
            return T{};
        }

        if constexpr (std::is_integral_v<T>) {
            return common_func::string_to_uint64(value->second.c_str());
        } else {
            return value->second;
        }
    }

private:
    std::map<std::string, std::string> configs_;
};
}  // namespace sunny

config.cc

#include "config.h"

namespace sunny {
config& config::instance() {
    static config object;
    return object;
}

bool config::start(std::string_view file_name) {
    std::string path = common_func::get_cur_exe_dir() + "/" + file_name.data();
    std::ifstream file_handle(path);
    assert(!file_handle.fail());

    std::string data;
    while (getline(file_handle, data)) {
        std::string::size_type pos = data.find('=');
        if (pos == std::string::npos) {
            continue;
        }

        std::string key = data.substr(0, pos);
        std::string value = data.substr(pos + 1);
        common_func::trim(key);
        common_func::trim(value);
        configs_.insert({key, value});
    }

    file_handle.close();
    return true;
}
}  // namespace sunny