From 1335192387e3978562c093d504f1f69f8679533b Mon Sep 17 00:00:00 2001 From: Soumik15630m Date: Sun, 15 Feb 2026 23:33:11 +0530 Subject: [PATCH] Revisioned changes --- CMakeLists.txt | 14 +++---- include/fmt/fmt-c.h | 27 +++++++------ src/fmt-c.cc | 93 +++++++++++++----------------------------- test/test_c.c | 99 +++++++++++++++++++++------------------------ 4 files changed, 94 insertions(+), 139 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 99a0ff74..aabe66cd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -537,7 +537,6 @@ endif () add_library(fmt_c STATIC src/fmt-c.cc) target_compile_features(fmt_c PUBLIC cxx_std_11) -target_compile_definitions(fmt_c PUBLIC FMT_C_STATIC) target_link_libraries(fmt_c PUBLIC fmt::fmt) target_include_directories(fmt_c PUBLIC @@ -570,16 +569,13 @@ if(FMT_TEST) add_executable(test-c-api test/test_c.c) target_link_libraries(test-c-api PRIVATE fmt::fmt_c) - #needed for c11(_generic) + set_target_properties(test-c-api PROPERTIES + C_STANDARD 11 + C_STANDARD_REQUIRED ON + ) if(MSVC) - target_compile_options(test-c-api PRIVATE /std:c11 /Zc:preprocessor) - else() - set_target_properties(test-c-api PROPERTIES - C_STANDARD 11 - C_STANDARD_REQUIRED ON - ) + target_compile_options(test-c-api PRIVATE /Zc:preprocessor) endif() add_test(NAME c-api-test COMMAND test-c-api) endif() - diff --git a/include/fmt/fmt-c.h b/include/fmt/fmt-c.h index d3b2d8af..34927bf1 100644 --- a/include/fmt/fmt-c.h +++ b/include/fmt/fmt-c.h @@ -1,15 +1,16 @@ #ifndef FMT_C_H #define FMT_C_H -#include -#include -#include +#ifdef __cplusplus +# define _Bool bool +#endif #include -#define FMT_C_MAX_ARGS 16 +void fmt_error_unsupported_type_detected(void); + +enum { fmt_c_max_args = 16 }; typedef enum { - fmt_ok = 0, fmt_err_exception = -1, fmt_err_memory = -2, fmt_err_invalid_arg = -3 @@ -34,29 +35,29 @@ typedef enum { typedef struct { fmt_type type; union { - int64_t i64; - uint64_t u64; + long long i64; + unsigned long long u64; float f32; double f64; long double f128; const char* str; const void* ptr; // Used for FMT_PTR and custom data - int bool_val; - int char_val; + _Bool bool_val; + char char_val; } value; } fmt_arg; int fmt_vformat(char* buffer, size_t capacity, const char* format_str, const fmt_arg* args, size_t arg_count); -static inline fmt_arg fmt_from_int(int64_t x) { +static inline fmt_arg fmt_from_int(long long x) { fmt_arg arg; arg.type = fmt_int; arg.value.i64 = x; return arg; } -static inline fmt_arg fmt_from_uint(uint64_t x) { +static inline fmt_arg fmt_from_uint(unsigned long long x) { fmt_arg arg; arg.type = fmt_uint; arg.value.u64 = x; @@ -98,7 +99,7 @@ static inline fmt_arg fmt_from_ptr(const void* x) { return arg; } -static inline fmt_arg fmt_from_bool(bool x) { +static inline fmt_arg fmt_from_bool(_Bool x) { fmt_arg arg; arg.type = fmt_bool; arg.value.bool_val = x; @@ -143,7 +144,7 @@ static inline fmt_arg fmt_from_char(int x) { const char*: fmt_from_str, \ void*: fmt_from_ptr, \ const void*: fmt_from_ptr, \ - default: fmt_from_ptr)(x) + default: fmt_error_unsupported_type_detected)(x) # define FMT_CAT(a, b) FMT_CAT_(a, b) # define FMT_CAT_(a, b) a##b diff --git a/src/fmt-c.cc b/src/fmt-c.cc index b3e5cbf0..dd5130b6 100644 --- a/src/fmt-c.cc +++ b/src/fmt-c.cc @@ -2,91 +2,54 @@ #include -#include #include -#include -#include -#include extern "C" { -using Context = fmt::format_context; +using format_arg = fmt::basic_format_arg; -static bool populate_store(fmt::basic_format_arg* out, - const fmt_arg* c_args, size_t arg_count) { - if (arg_count > FMT_C_MAX_ARGS) { +static bool populate_store(format_arg* out, const fmt_arg* c_args, + size_t arg_count) { + if (arg_count > fmt_c_max_args) { return false; } for (size_t i = 0; i < arg_count; ++i) { switch (c_args[i].type) { - case fmt_int: - out[i] = fmt::basic_format_arg(c_args[i].value.i64); - break; - case fmt_uint: - out[i] = fmt::basic_format_arg(c_args[i].value.u64); - break; - case fmt_float: - out[i] = fmt::basic_format_arg(c_args[i].value.f32); - break; - case fmt_double: - out[i] = fmt::basic_format_arg(c_args[i].value.f64); - break; - case fmt_long_double: - out[i] = fmt::basic_format_arg(c_args[i].value.f128); - break; - case fmt_ptr: - out[i] = fmt::basic_format_arg(c_args[i].value.ptr); - break; - case fmt_char: - out[i] = fmt::basic_format_arg( - static_cast(c_args[i].value.char_val)); - break; - case fmt_bool: - out[i] = fmt::basic_format_arg(c_args[i].value.bool_val != 0); - break; - case fmt_string: { - const char* s = c_args[i].value.str ? c_args[i].value.str : "(null)"; - out[i] = fmt::basic_format_arg(fmt::string_view(s)); - break; - } - - default: return false; + case fmt_int: out[i] = c_args[i].value.i64; break; + case fmt_uint: out[i] = c_args[i].value.u64; break; + case fmt_float: out[i] = c_args[i].value.f32; break; + case fmt_double: out[i] = c_args[i].value.f64; break; + case fmt_long_double: out[i] = c_args[i].value.f128; break; + case fmt_ptr: out[i] = c_args[i].value.ptr; break; + case fmt_char: out[i] = c_args[i].value.char_val; break; + case fmt_bool: out[i] = c_args[i].value.bool_val; break; + case fmt_string: out[i] = c_args[i].value.str; break; + default: return false; } } return true; } + int fmt_vformat(char* buffer, size_t capacity, const char* format_str, const fmt_arg* args, size_t arg_count) { assert(format_str); - fmt::basic_format_arg format_args[FMT_C_MAX_ARGS]; + format_arg format_args[fmt_c_max_args]; - try { - if (arg_count > 0) { - assert(args); - if (!populate_store(format_args, args, arg_count)) { - return fmt_err_exception; - } + if (arg_count > 0) { + assert(args); + if (!populate_store(format_args, args, arg_count)) { + return fmt_err_invalid_arg; } - - auto format_args_view = fmt::basic_format_args( - format_args, static_cast(arg_count)); - - size_t write_capacity = (capacity > 0) ? capacity - 1 : 0; - auto result = - fmt::vformat_to_n(buffer, write_capacity, format_str, format_args_view); - - if (capacity > 0) { - buffer[result.size] = '\0'; - } - return static_cast(result.size); - - } catch (const std::bad_alloc&) { - return fmt_err_memory; - } catch (...) { - return fmt_err_exception; } + + auto format_args_view = fmt::basic_format_args( + format_args, static_cast(arg_count)); + + auto result = + fmt::vformat_to_n(buffer, capacity, format_str, format_args_view); + return static_cast(result.size); } -} // extern "C" +} // extern "C" \ No newline at end of file diff --git a/test/test_c.c b/test/test_c.c index 99c3a4ae..7924bc23 100644 --- a/test/test_c.c +++ b/test/test_c.c @@ -1,9 +1,11 @@ /* Test suite for fmt C API */ +#include #include #include #include #include "fmt/fmt-c.h" + #define ASSERT_STR_EQ(actual, expected) \ do { \ if (strcmp(actual, expected) != 0) { \ @@ -31,43 +33,58 @@ } \ } while (0) +// Helper to manually null-terminate buffer after formatting +void terminate(char* buf, int size, size_t capacity) { + if (size >= 0 && (size_t)size < capacity) { + buf[size] = '\0'; + } else if (capacity > 0) { + buf[capacity - 1] = '\0'; + } +} + void test_basic_integer(void) { char buf[100]; int ret = fmt_format(buf, sizeof(buf), "Number: {}", 42); + terminate(buf, ret, sizeof(buf)); ASSERT_STR_EQ(buf, "Number: 42"); ASSERT_INT_EQ(ret, 10); } void test_multiple_integers(void) { char buf[100]; - fmt_format(buf, sizeof(buf), "{} + {} = {}", 1, 2, 3); + int ret = fmt_format(buf, sizeof(buf), "{} + {} = {}", 1, 2, 3); + terminate(buf, ret, sizeof(buf)); ASSERT_STR_EQ(buf, "1 + 2 = 3"); } void test_unsigned_integers(void) { char buf[100]; unsigned int x = 4294967295U; - fmt_format(buf, sizeof(buf), "{}", x); + int ret = fmt_format(buf, sizeof(buf), "{}", x); + terminate(buf, ret, sizeof(buf)); ASSERT_STR_EQ(buf, "4294967295"); } void test_floating_point(void) { char buf[100]; - fmt_format(buf, sizeof(buf), "Pi = {}", 3.14159); + int ret = fmt_format(buf, sizeof(buf), "Pi = {}", 3.14159); + terminate(buf, ret, sizeof(buf)); ASSERT_TRUE(strncmp(buf, "Pi = 3.14159", 12) == 0); } void test_float_type(void) { char buf[100]; float f = 1.234f; - fmt_format(buf, sizeof(buf), "Float: {:.3f}", f); + int ret = fmt_format(buf, sizeof(buf), "Float: {:.3f}", f); + terminate(buf, ret, sizeof(buf)); ASSERT_STR_EQ(buf, "Float: 1.234"); } void test_long_double_type(void) { char buf[100]; long double ld = 12345.6789L; - fmt_format(buf, sizeof(buf), "{:.4f}", ld); + int ret = fmt_format(buf, sizeof(buf), "{:.4f}", ld); + terminate(buf, ret, sizeof(buf)); ASSERT_STR_EQ(buf, "12345.6789"); } @@ -77,55 +94,55 @@ void test_mixed_floating_types(void) { double d = 2.5; long double ld = 3.5L; - fmt_format(buf, sizeof(buf), "{} {} {}", f, d, ld); + int ret = fmt_format(buf, sizeof(buf), "{} {} {}", f, d, ld); + terminate(buf, ret, sizeof(buf)); ASSERT_STR_EQ(buf, "1.5 2.5 3.5"); } void test_strings(void) { char buf[100]; - fmt_format(buf, sizeof(buf), "Hello, {}!", "from fmt!"); + int ret = fmt_format(buf, sizeof(buf), "Hello, {}!", "from fmt!"); + terminate(buf, ret, sizeof(buf)); ASSERT_STR_EQ(buf, "Hello, from fmt!!"); } -void test_null_string(void) { - char buf[100]; - const char* null_str = NULL; - fmt_format(buf, sizeof(buf), "{}", null_str); - ASSERT_STR_EQ(buf, "(null)"); -} - void test_pointers(void) { char buf[100]; void* ptr = (void*)0x12345678; - fmt_format(buf, sizeof(buf), "{}", ptr); + int ret = fmt_format(buf, sizeof(buf), "{}", ptr); + terminate(buf, ret, sizeof(buf)); ASSERT_TRUE(strstr(buf, "12345678") != NULL); } void test_booleans(void) { char buf[100]; - fmt_format(buf, sizeof(buf), "{} {}", (bool)true, (bool)false); + int ret = fmt_format(buf, sizeof(buf), "{} {}", (bool)true, (bool)false); + terminate(buf, ret, sizeof(buf)); ASSERT_STR_EQ(buf, "true false"); } void test_characters(void) { char buf[100]; - fmt_format(buf, sizeof(buf), "Char: {}", (char)'A'); + int ret = fmt_format(buf, sizeof(buf), "Char: {}", (char)'A'); + terminate(buf, ret, sizeof(buf)); ASSERT_STR_EQ(buf, "Char: A"); } void test_mixed_types(void) { char buf[100]; - fmt_format(buf, sizeof(buf), "{} {} {} {}", 42, 3.14, "text", (bool)true); + int ret = + fmt_format(buf, sizeof(buf), "{} {} {} {}", 42, 3.14, "text", (bool)true); + terminate(buf, ret, sizeof(buf)); ASSERT_TRUE(strstr(buf, "42") != NULL); ASSERT_TRUE(strstr(buf, "3.14") != NULL); ASSERT_TRUE(strstr(buf, "text") != NULL); ASSERT_TRUE(strstr(buf, "true") != NULL); } + void test_zero_arguments(void) { char buf[100]; - // fmt_format(buf, sizeof(buf), "No arguments"); - fmt_vformat(buf, sizeof(buf), "No arguments", NULL, - 0); // strict compiler check bypass + int ret = fmt_vformat(buf, sizeof(buf), "No arguments", NULL, 0); + terminate(buf, ret, sizeof(buf)); ASSERT_STR_EQ(buf, "No arguments"); } @@ -134,51 +151,32 @@ void test_buffer_size_query(void) { ASSERT_INT_EQ(size, 15); } -void test_error_invalid_format(void) { - char buf[100]; - int ret = fmt_format(buf, sizeof(buf), "{:invalid}", 1); - ASSERT_TRUE(ret < 0); -} - void test_long_strings(void) { char buf[1000]; const char* long_str = "This is a very long string that contains a lot of text " "to test the buffer handling capabilities of the formatter"; - fmt_format(buf, sizeof(buf), "Message: {}", long_str); + int ret = fmt_format(buf, sizeof(buf), "Message: {}", long_str); + terminate(buf, ret, sizeof(buf)); ASSERT_TRUE(strstr(buf, long_str) != NULL); } void test_multiple_calls(void) { char buf[100]; - fmt_format(buf, sizeof(buf), "{} {}", 1, 2); + int ret = fmt_format(buf, sizeof(buf), "{} {}", 1, 2); + terminate(buf, ret, sizeof(buf)); ASSERT_STR_EQ(buf, "1 2"); - fmt_format(buf, sizeof(buf), "{} {}", "hello", 3.14); + ret = fmt_format(buf, sizeof(buf), "{} {}", "hello", 3.14); + terminate(buf, ret, sizeof(buf)); ASSERT_TRUE(strstr(buf, "hello") != NULL); - fmt_format(buf, sizeof(buf), "{}", (bool)true); + ret = fmt_format(buf, sizeof(buf), "{}", (bool)true); + terminate(buf, ret, sizeof(buf)); ASSERT_STR_EQ(buf, "true"); } -void test_all_integer_types(void) { - char buf[200]; - short s = 100; - int i = 200; - long l = 300L; - long long ll = 400LL; - unsigned short us = 500; - unsigned int ui = 600; - unsigned long ul = 700UL; - unsigned long long ull = 800ULL; - - fmt_format(buf, sizeof(buf), "{} {} {} {} {} {} {} {}", s, i, l, ll, us, ui, - ul, ull); - ASSERT_TRUE(strstr(buf, "100") != NULL); - ASSERT_TRUE(strstr(buf, "800") != NULL); -} - int main(void) { printf("=== Running fmt C API Tests ===\n\n"); @@ -190,18 +188,15 @@ int main(void) { test_long_double_type(); test_mixed_floating_types(); test_strings(); - test_null_string(); test_pointers(); test_booleans(); test_characters(); test_mixed_types(); test_zero_arguments(); test_buffer_size_query(); - test_error_invalid_format(); test_long_strings(); test_multiple_calls(); - test_all_integer_types(); printf("\n=== All tests passed! ===\n"); return 0; -} +} \ No newline at end of file