Last active
May 31, 2024 02:47
-
-
Save dylanliuh2o/66100ab5af2aa23eb144c7bafc5eed1f to your computer and use it in GitHub Desktop.
Mode Maintainable Cpp String Conversion
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
cmake_minimum_required(VERSION 3.20) | |
project(string_conversion_exception) | |
set(CMAKE_CXX_STANDARD_REQUIRED 17) | |
find_package(Boost 1.74 REQUIRED) | |
if (Boost_FOUND) | |
message("Boost found") | |
add_executable(main main.cpp) | |
else() | |
message("Boost not found") | |
endif() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <iostream> | |
#include <sstream> | |
#include <stdexcept> | |
#include <string> | |
#include <vector> | |
#include <map> | |
#include <boost/format.hpp> | |
using boost::format ; | |
template <typename... Args> | |
void | |
log_none(const std::string &prefix, format fmt, Args... args) | |
{ | |
std::ostringstream oss ; | |
if (!prefix.empty()) { | |
oss << format("[%s] ") % prefix ; | |
} | |
oss << (fmt % ... % args) ; | |
std::cout << oss.str() << std::endl ; | |
} | |
template <typename... Args> | |
void | |
log_info(const std::string &prefix, format fmt, Args... args) | |
{ | |
std::ostringstream oss ; | |
if (!prefix.empty()) { | |
oss << format("[%s] ") % prefix ; | |
} | |
oss << "INFO: " ; | |
oss << (fmt % ... % args) ; | |
std::cout << oss.str() << std::endl ; | |
} | |
template <typename... Args> | |
void | |
log_warning(const std::string &prefix, format fmt, Args... args) | |
{ | |
std::ostringstream oss ; | |
if (!prefix.empty()) { | |
oss << format("[%s] ") % prefix ; | |
} | |
oss << "WARNING: " ; | |
oss << (fmt % ... % args) ; | |
std::cout << oss.str() << std::endl ; | |
} | |
template <typename... Args> | |
void | |
log_error(const std::string &prefix, format fmt, Args... args) | |
{ | |
std::ostringstream oss ; | |
if (!prefix.empty()) { | |
oss << format("[%s] ") % prefix ; | |
} | |
oss << "ERROR: " ; | |
oss << (fmt % ... % args) ; | |
std::cout << oss.str() << std::endl ; | |
} | |
struct Data | |
{ | |
int total = -1 ; | |
int covered = -1 ; | |
double percentage = -1 ; | |
int count = -1 ; | |
double x = -1 ; | |
double y = -1 ; | |
double z = -1 ; | |
int pos = -1 ; | |
} ; | |
int main(int argc, char *argv[]) | |
{ | |
bool bad_data = false ; | |
if (argc > 1 && std::string(argv[1]) == "--bad_data") { | |
bad_data = true ; | |
} | |
std::map<std::string, std::string> data_map ; | |
if (bad_data) { | |
std::map<std::string, std::string> m { | |
{ "total", "fj239" }, | |
{ "covered", "aslkdjf" }, | |
{ "percentage", "x1.0" }, | |
{ "count", "777777777777777777777777777777777777777777777777777777777777" }, | |
{ "x", "h3.2" }, | |
{ "y", "x2.1" }, | |
{ "z", "d1.8" }, | |
{ "pos", "h51" }, | |
} ; | |
data_map = m ; | |
} else { | |
std::map<std::string, std::string> m { | |
{ "total", "239" }, | |
{ "covered", "123" }, | |
{ "percentage", "51.5" }, | |
{ "count", "7" }, | |
{ "x", "3.2" }, | |
{ "y", "2.1" }, | |
{ "z", "1.8" }, | |
{ "pos", "51" }, | |
} ; | |
data_map = m ; | |
} | |
// too verbose, hard to maintain | |
{ | |
Data data_1 ; | |
try { | |
data_1.total = data_map["total"].empty() ? -1 : std::stoi(data_map["total"]) ; | |
} catch (std::invalid_argument &e) { | |
log_error("MAIN", format("Failed to convert '%s' into int, might contains unrecognized syntax!"), data_map["total"]) ; | |
} catch (std::out_of_range &e) { | |
log_error("MAIN", format("Failed to convert '%s' into int, value is out of range!"), data_map["total"]) ; | |
} | |
try { | |
data_1.covered = data_map["covered"].empty() ? -1 : std::stoi(data_map["covered"]) ; | |
} catch (std::invalid_argument &e) { | |
log_error("MAIN", format("Failed to convert '%s' into int, might contains unrecognized syntax!"), data_map["covered"]) ; | |
} catch (std::out_of_range &e) { | |
log_error("MAIN", format("Failed to convert '%s' into int, value is out of range!"), data_map["covered"]) ; | |
} | |
try { | |
data_1.percentage = data_map["percentage"].empty() ? -1 : std::stod(data_map["percentage"]) ; | |
} catch (std::invalid_argument &e) { | |
log_error("MAIN", format("Failed to convert '%s' into double, might contains unrecognized syntax!"), data_map["percentage"]) ; | |
} catch (std::out_of_range &e) { | |
log_error("MAIN", format("Failed to convert '%s' into double, value is out of range!"), data_map["percentage"]) ; | |
} | |
try { | |
data_1.count = data_map["count"].empty() ? -1 : std::stoi(data_map["count"]) ; | |
} catch (std::invalid_argument &e) { | |
log_error("MAIN", format("Failed to convert '%s' into int, might contains unrecognized syntax!"), data_map["count"]) ; | |
} catch (std::out_of_range &e) { | |
log_error("MAIN", format("Failed to convert '%s' into int, value is out of range!"), data_map["count"]) ; | |
} | |
try { | |
data_1.x = data_map["x"].empty() ? -1 : std::stod(data_map["x"]) ; | |
} catch (std::invalid_argument &e) { | |
log_error("MAIN", format("Failed to convert '%s' into double, might contains unrecognized syntax!"), data_map["x"]) ; | |
} catch (std::out_of_range &e) { | |
log_error("MAIN", format("Failed to convert '%s' into double, value is out of range!"), data_map["x"]) ; | |
} | |
try { | |
data_1.y = data_map["y"].empty() ? -1 : std::stod(data_map["y"]) ; | |
} catch (std::invalid_argument &e) { | |
log_error("MAIN", format("Failed to convert '%s' into double, might contains unrecognized syntax!"), data_map["y"]) ; | |
} catch (std::out_of_range &e) { | |
log_error("MAIN", format("Failed to convert '%s' into double, value is out of range!"), data_map["y"]) ; | |
} | |
try { | |
data_1.z = data_map["z"].empty() ? -1 : std::stod(data_map["z"]) ; | |
} catch (std::invalid_argument &e) { | |
log_error("MAIN", format("Failed to convert '%s' into double, might contains unrecognized syntax!"), data_map["z"]) ; | |
} catch (std::out_of_range &e) { | |
log_error("MAIN", format("Failed to convert '%s' into double, value is out of range!"), data_map["z"]) ; | |
} | |
try { | |
data_1.pos = data_map["pos"].empty() ? -1 : std::stoi(data_map["pos"]) ; | |
} catch (std::invalid_argument &e) { | |
log_error("MAIN", format("Failed to convert '%s' into int, might contains unrecognized syntax!"), data_map["pos"]) ; | |
} catch (std::out_of_range &e) { | |
log_error("MAIN", format("Failed to convert '%s' into int, value is out of range!"), data_map["pos"]) ; | |
} | |
log_info("MAIN", format("data_1: { total: %d, covered: %d, percentage: %.2f%%, count: %d, x: %.2f, y: %.2f, z: %2.f, pos: %d }"), data_1.total, data_1.covered, data_1.percentage, data_1.count, data_1.x, data_1.y, data_1.z, data_1.pos) ; | |
} | |
log_none("", format("")) ; | |
// more tidy, more readable, easier to maintain | |
{ | |
enum class TargetType { Int = 0, Double } ; | |
std::map<TargetType, std::string> type_to_string { | |
{ TargetType::Int, "int" }, | |
{ TargetType::Double, "double" }, | |
} ; | |
Data data_2 ; | |
std::vector<std::string> data { data_map["total"], data_map["percentage"], data_map["covered"], data_map["count"], data_map["x"], data_map["y"], data_map["z"], data_map["pos"] } ; | |
std::vector<TargetType> data_types { TargetType::Int, TargetType::Double, TargetType::Int, TargetType::Int, TargetType::Double, TargetType::Double, TargetType::Double, TargetType::Int } ; | |
for (decltype(data.size()) i = 0; i < data.size(); i++) { | |
std::string raw_data = data[i] ; | |
TargetType target_type = data_types[i] ; | |
int value_int = -1 ; | |
double value_double = -1 ; | |
switch (target_type) { | |
case TargetType::Int: | |
try { | |
value_int = raw_data.empty() ? -1 : std::stoi(raw_data) ; | |
} catch (std::invalid_argument &e) { | |
log_error("MAIN", format("Failed to convert '%s' into %s, might contains unrecognized syntax!"), raw_data, type_to_string[target_type]) ; | |
} catch (std::out_of_range &e) { | |
log_error("MAIN", format("Failed to convert '%s' into %s, value is out of range!"), raw_data, type_to_string[target_type]) ; | |
} | |
break ; | |
case TargetType::Double: | |
try { | |
value_double = raw_data.empty() ? -1 : std::stod(raw_data) ; | |
} catch (std::invalid_argument &e) { | |
log_error("MAIN", format("Failed to convert '%s' into %s, might contains unrecognized syntax!"), raw_data, type_to_string[target_type]) ; | |
} catch (std::out_of_range &e) { | |
log_error("MAIN", format("Failed to convert '%s' into %s, value is out of range!"), raw_data, type_to_string[target_type]) ; | |
} | |
break ; | |
default: | |
log_error("MAIN", format("Unexpect target_type '%d'!"), static_cast<int>(target_type)) ; | |
break ; | |
} | |
switch (i) { | |
case 0: data_2.total = value_int ; break ; | |
case 1: data_2.percentage = value_double ; break ; | |
case 2: data_2.covered = value_int ; break ; | |
case 3: data_2.count = value_int ; break ; | |
case 4: data_2.x = value_double ; break ; | |
case 5: data_2.y = value_double ; break ; | |
case 6: data_2.z = value_double ; break ; | |
case 7: data_2.pos = value_int ; break ; | |
default: break ; | |
} | |
} | |
log_info("MAIN", format("data_2: { total: %d, covered: %d, percentage: %.2f%%, count: %d, x: %.2f, y: %.2f, z: %2.f, pos: %d }"), data_2.total, data_2.covered, data_2.percentage, data_2.count, data_2.x, data_2.y, data_2.z, data_2.pos) ; | |
} | |
return 0 ; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment