From f8a83d694b2d19d5d2b2945cca37dadad944c99b Mon Sep 17 00:00:00 2001 From: Andrew Eckel Date: Wed, 19 Aug 2020 23:09:41 -0400 Subject: [PATCH] Add basic C++ support This commit adds C++ support with the following modifications: - Remove the use of C-only features including designated initializers and non-strict prototypes. - Add spaces between literals and string macros to silence the C++-only -Wliteral-suffix warning. - Conditionally use template specialization instead of tentative definitions to implement optional setup/teardown functions. Note that no C++-specific features have been added. The goal of this commit is to simply enable compiling in C++ mode. --- README.md | 2 +- ctest.h | 75 +++++++++++++++++++++++++++++++++++++++++++------------ 2 files changed, 60 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index c4ebde4..d23128e 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # CTEST -ctest is a unit test framework for software written in C. +ctest is a unit test framework for software written in C/C++. Features: * adding tests with minimal hassle (no manual adding to suites or testlists!) diff --git a/ctest.h b/ctest.h index 49e742f..1dbe17f 100644 --- a/ctest.h +++ b/ctest.h @@ -16,6 +16,10 @@ #ifndef CTEST_H #define CTEST_H +#ifdef __cplusplus +extern "C" { +#endif + #ifdef __GNUC__ #define CTEST_IMPL_FORMAT_PRINTF(a, b) __attribute__ ((format(printf, a, b))) #else @@ -25,9 +29,16 @@ #include /* intmax_t, uintmax_t, PRI* */ #include /* size_t */ +typedef void (*ctest_nullary_run_func)(void); +typedef void (*ctest_unary_run_func)(void*); typedef void (*ctest_setup_func)(void*); typedef void (*ctest_teardown_func)(void*); +union ctest_run_func_union { + ctest_nullary_run_func nullary; + ctest_unary_run_func unary; +}; + #define CTEST_IMPL_PRAGMA(x) _Pragma (#x) #if defined(__GNUC__) @@ -50,12 +61,10 @@ typedef void (*ctest_teardown_func)(void*); #define CTEST_IMPL_DIAG_POP() #endif -CTEST_IMPL_DIAG_PUSH_IGNORED(strict-prototypes) - struct ctest { const char* ssname; // suite name const char* ttname; // test name - void (*run)(); + union ctest_run_func_union run; void* data; ctest_setup_func* setup; @@ -66,8 +75,6 @@ struct ctest { unsigned int magic; }; -CTEST_IMPL_DIAG_POP() - #define CTEST_IMPL_NAME(name) ctest_##name #define CTEST_IMPL_FNAME(sname, tname) CTEST_IMPL_NAME(sname##_##tname##_run) #define CTEST_IMPL_TNAME(sname, tname) CTEST_IMPL_NAME(sname##_##tname) @@ -75,8 +82,10 @@ CTEST_IMPL_DIAG_POP() #define CTEST_IMPL_DATA_TNAME(sname, tname) CTEST_IMPL_NAME(sname##_##tname##_data) #define CTEST_IMPL_SETUP_FNAME(sname) CTEST_IMPL_NAME(sname##_setup) #define CTEST_IMPL_SETUP_FPNAME(sname) CTEST_IMPL_NAME(sname##_setup_ptr) +#define CTEST_IMPL_SETUP_TPNAME(sname, tname) CTEST_IMPL_NAME(sname##_##tname##_setup_ptr) #define CTEST_IMPL_TEARDOWN_FNAME(sname) CTEST_IMPL_NAME(sname##_teardown) #define CTEST_IMPL_TEARDOWN_FPNAME(sname) CTEST_IMPL_NAME(sname##_teardown_ptr) +#define CTEST_IMPL_TEARDOWN_TPNAME(sname, tname) CTEST_IMPL_NAME(sname##_##tname##_teardown_ptr) #define CTEST_IMPL_MAGIC (0xdeadbeef) #ifdef __APPLE__ @@ -87,14 +96,43 @@ CTEST_IMPL_DIAG_POP() #define CTEST_IMPL_STRUCT(sname, tname, tskip, tdata, tsetup, tteardown) \ static struct ctest CTEST_IMPL_TNAME(sname, tname) CTEST_IMPL_SECTION = { \ - .ssname=#sname, \ - .ttname=#tname, \ - .run = CTEST_IMPL_FNAME(sname, tname), \ - .data = tdata, \ - .setup = (ctest_setup_func*) tsetup, \ - .teardown = (ctest_teardown_func*) tteardown, \ - .skip = tskip, \ - .magic = CTEST_IMPL_MAGIC } + #sname, \ + #tname, \ + { (ctest_nullary_run_func) CTEST_IMPL_FNAME(sname, tname) }, \ + tdata, \ + (ctest_setup_func*) tsetup, \ + (ctest_teardown_func*) tteardown, \ + tskip, \ + CTEST_IMPL_MAGIC, \ + } + +#ifdef __cplusplus + +#define CTEST_SETUP(sname) \ + template <> void CTEST_IMPL_SETUP_FNAME(sname)(struct CTEST_IMPL_DATA_SNAME(sname)* data) + +#define CTEST_TEARDOWN(sname) \ + template <> void CTEST_IMPL_TEARDOWN_FNAME(sname)(struct CTEST_IMPL_DATA_SNAME(sname)* data) + +#define CTEST_DATA(sname) \ + template void CTEST_IMPL_SETUP_FNAME(sname)(T* data) { } \ + template void CTEST_IMPL_TEARDOWN_FNAME(sname)(T* data) { } \ + struct CTEST_IMPL_DATA_SNAME(sname) + +#define CTEST_IMPL_CTEST(sname, tname, tskip) \ + static void CTEST_IMPL_FNAME(sname, tname)(void); \ + CTEST_IMPL_STRUCT(sname, tname, tskip, NULL, NULL, NULL); \ + static void CTEST_IMPL_FNAME(sname, tname)(void) + +#define CTEST_IMPL_CTEST2(sname, tname, tskip) \ + static struct CTEST_IMPL_DATA_SNAME(sname) CTEST_IMPL_DATA_TNAME(sname, tname); \ + static void CTEST_IMPL_FNAME(sname, tname)(struct CTEST_IMPL_DATA_SNAME(sname)* data); \ + static void (*CTEST_IMPL_SETUP_TPNAME(sname, tname))(struct CTEST_IMPL_DATA_SNAME(sname)*) = &CTEST_IMPL_SETUP_FNAME(sname); \ + static void (*CTEST_IMPL_TEARDOWN_TPNAME(sname, tname))(struct CTEST_IMPL_DATA_SNAME(sname)*) = &CTEST_IMPL_TEARDOWN_FNAME(sname); \ + CTEST_IMPL_STRUCT(sname, tname, tskip, &CTEST_IMPL_DATA_TNAME(sname, tname), &CTEST_IMPL_SETUP_TPNAME(sname, tname), &CTEST_IMPL_TEARDOWN_TPNAME(sname, tname)); \ + static void CTEST_IMPL_FNAME(sname, tname)(struct CTEST_IMPL_DATA_SNAME(sname)* data) + +#else #define CTEST_SETUP(sname) \ static void CTEST_IMPL_SETUP_FNAME(sname)(struct CTEST_IMPL_DATA_SNAME(sname)* data); \ @@ -123,6 +161,7 @@ CTEST_IMPL_DIAG_POP() CTEST_IMPL_STRUCT(sname, tname, tskip, &CTEST_IMPL_DATA_TNAME(sname, tname), &CTEST_IMPL_SETUP_FPNAME(sname), &CTEST_IMPL_TEARDOWN_FPNAME(sname)); \ static void CTEST_IMPL_FNAME(sname, tname)(struct CTEST_IMPL_DATA_SNAME(sname)* data) +#endif void CTEST_LOG(const char* fmt, ...) CTEST_IMPL_FORMAT_PRINTF(1, 2); void CTEST_ERR(const char* fmt, ...) CTEST_IMPL_FORMAT_PRINTF(1, 2); // doesn't return @@ -427,7 +466,7 @@ static uint64_t getCurrentTime(void) { static void color_print(const char* color, const char* text) { if (color_output) - printf("%s%s"ANSI_NORMAL"\n", color, text); + printf("%s%s" ANSI_NORMAL "\n", color, text); else printf("%s\n", text); } @@ -512,9 +551,9 @@ __attribute__((no_sanitize_address)) int ctest_main(int argc, const char *argv[] if (result == 0) { if (test->setup && *test->setup) (*test->setup)(test->data); if (test->data) - test->run(test->data); + test->run.unary(test->data); else - test->run(); + test->run.nullary(); if (test->teardown && *test->teardown) (*test->teardown)(test->data); // if we got here it's ok #ifdef CTEST_COLOR_OK @@ -543,5 +582,9 @@ __attribute__((no_sanitize_address)) int ctest_main(int argc, const char *argv[] #endif +#ifdef __cplusplus +} +#endif + #endif