allocate_shared with boost pool allocator - shared-ptr

I am trying to use std::allocate_shared with boost pool allocator, the programs runs fine except it crashes with segfault in shared pointer destructor after some time. I am wondering whether my implementation has a bug. i am pasting the allocator here, I am using gcc-7.2 compiler on ubuntu. please advise.
#pragma once
#include <memory>
#include <utility>
#include <boost/pool/pool_alloc.hpp>
#include <assert.h>
#include <cstddef>
#include <type_traits>
#include <scoped_allocator>
template <typename T = void>
struct _shpAllocator
{
template <typename U> friend struct _shpAllocator;
using value_type = T;
using pointer = T *;
using propagate_on_container_copy_assignment = std::true_type;
using propagate_on_container_move_assignment = std::true_type;
using propagate_on_container_swap = std::true_type;
using is_always_equal = std::true_type;
explicit _shpAllocator() {}
template <typename U> _shpAllocator(_shpAllocator<U> const & rhs) {}
pointer allocate(std::size_t n)
{
std::cerr<<"_shpAllocator() typename : "<<typeid(T).name()<<std::endl;
auto p = __fpAlloc.allocate ( n );
return p;
}
void deallocate(pointer p, std::size_t n)
{
__fpAlloc.deallocate ( p, n );
return;
}
template <typename U> bool operator==(_shpAllocator<U> const & rhs) const { return true; }
template <typename U> bool operator!=(_shpAllocator<U> const & rhs) const { return false; }
private:
static boost::pool_allocator<T, boost::default_user_allocator_new_delete, boost::details::pool::null_mutex> __fpAlloc;
static sLock __lock;
};
template<typename T> boost::pool_allocator<T, boost::default_user_allocator_new_delete, boost::details::pool::null_mutex> _shpAllocator<T>::__fpAlloc;
template<typename T> sLock _shpAllocator<T>::__lock;
template<typename T> using shpAllocator = std::scoped_allocator_adaptor<_shpAllocator<T>>;
template<typename T> using sharedObjectAllocator = shpAllocator<T>;
crash trace.
Program terminated with signal SIGSEGV, Segmentation fault.
#0 0x0000000000767cc5 in std::_Sp_counted_ptr_inplace<

Related

GTest | Seg faults when testing insertion of randomly generated values at random index to a dynamic array

I created a custom generic DynamicArray class where everything works fine except when testing an add() method in which I am adding element ats a specific index.
I am using gtest framework for testing, more specifically, I am using type-parametrized tests for creating test cases. I run the tests against these types (int, int*, char*, char*, std::string), I created functions that generate random values for me and I use them in my test cases.
The problem is that sometimes I get seg faults and some other times I don't.
Here is the structure of my project:
build/
include/
structs/
DynamicArray.h
src/
main.cpp
tests/
TestCases/
DynamicArrayTest.cpp
TestClasses/
DynamicArrayTest.h
TestHelpers/
generators.h
test_runner.cpp
CMakeLists.txt
CMakeLists.txt:
# cmake version and project info
############################################################################
# Variables
# Compile flags
SET(GCC_DEBUGGING_COMPILE_FLAGS "-Wall -ggdb3")
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${GCC_DEBUGGING_COMPILE_FLAGS}")
set(SRCS
src/main.cpp
)
set(EXEC_SRCS
src/main.cpp
)
set(HEADERS
include/structs/structures.h
)
set(TEST_SRCS
tests/TestCases/DynamicArrayTest.cpp
tests/test_runner.cpp
)
############################################################################
# Gtest
include(FetchContent)
FetchContent_Declare(
googletest
URL https://github.com/google/googletest/archive/03597a01ee50ed33e9dfd640b249b4be3799d395.zip
)
# For Windows: Prevent overriding the parent project's compiler/linker settings
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
FetchContent_MakeAvailable(googletest)
############################################################################
##
# Build targets
##
if(${PROJECT_NAME}_BUILD_EXECUTABLE)
add_executable(${PROJECT_NAME} ${EXEC_SRCS} ${SRCS})
if(${PROJECT_NAME}_ENABLE_UNIT_TESTING)
add_library(${PROJECT_NAME}_LIB ${HEADERS} ${SRCS})
endif()
elseif(${PROJECT_NAME}_BUILD_HEADERS_ONLY)
add_library(${PROJECT_NAME} INTERFACE)
else()
add_library(
${PROJECT_NAME}
${HEADERS}
${SRCS}
)
endif()
############################################################################
##
# Including Directories
##
target_include_directories(Structs
PUBLIC ${PROJECT_SOURCE_DIR}/include
)
############################################################################
##
# Installation
##
############################################################################
##
# Tests
##
enable_testing()
add_executable(StructsTest
${TEST_SRCS}
)
target_compile_options(StructsTest PRIVATE -ggdb3)
target_link_libraries(
StructsTest
GTest::gtest_main
)
target_include_directories(StructsTest
PUBLIC ${PROJECT_SOURCE_DIR}/tests
PRIVATE ${PROJECT_SOURCE_DIR}/include
)
include(GoogleTest)
gtest_discover_tests(StructsTest)
Code to be tested:
include/structs/DynamicArray.h
namespace structs
{
#define INITIAL_SIZE 10
template <typename T>
class DynamicArray
{
private:
size_t m_capacity;
size_t m_size;
T* m_data;
void resize();
public:
// constructors & destructors
DynamicArray();
DynamicArray(const DynamicArray&);
DynamicArray(T&);
DynamicArray(T&&);
DynamicArray(T*, size_t);
~DynamicArray();
// operations
void push_back(T);
void set(size_t, T);
void add(size_t, T);
T get(size_t);
T pop();
T remove(size_t);
size_t size();
size_t capacity();
bool is_empty();
void read();
void reset();
}; // DynamicArray class
}// namespace structs
// .. Impl
template<typename T>
structs::DynamicArray<T>::DynamicArray()
:m_capacity(INITIAL_SIZE),
m_data(new T[m_capacity]),
m_size(0)
{
if (m_data == nullptr)
throw std::bad_alloc();
m_size = 0;
}
template<typename T>
structs::DynamicArray<T>::DynamicArray( T* values, size_t size )
: DynamicArray()
{
for (size_t i = 0; i < size; i++)
{
this->push_back(*values);
values++;
}
}
template<typename T>
structs::DynamicArray<T>::~DynamicArray()
{
delete[] m_data;
}
template<typename T>
void structs::DynamicArray<T>::push_back( T value )
{
if (m_size >= m_capacity)
resize();
*(m_data + m_size) = value;
m_size ++;
}
template<typename T>
void structs::DynamicArray<T>::add( size_t index, T value )
{
if (index > m_size)
throw std::out_of_range("Invalid: Exceeds range, use set(size_t, T) instead");
if ( m_size == m_capacity )
resize();
for (long i = m_size - 1; i >= index; i--)
m_data[i+1] = m_data[i];
m_data[index] = value;
m_size ++;
}
Now to the test part:
I created a header file where I store the test fixture class and its implementation located in tests/TestClasses/*Test.h and I placed the test cases in tests/TestCases/*Test.cpp, and test_runner.cpp is the test driver.
DynamicArrayTest.h
The constructor SetUp() has a seperate implementation for each type, to make things a bit shorter I am going to only show the implementation of std::string type.
I assumed in the test case that resize() will not be called, and the array size will always be within the limit of the initial size it was created with to avoid adding a complexity overhead.
#pragma once
#include <iostream>
#include <random>
#include <cstdlib>
#include <ctime>
#include <type_traits>
#include "structs/DynamicArray.h"
#include "gtest/gtest.h"
#include "TestHelpers/generators.h"
#define TEST_INITIAL_SIZE 10
namespace structs
{
namespace test
{
template<typename T>
class DynamicArrayTest : public ::testing::Test
{
protected:
void SetUp() override;
void TearDown() override;
public:
structs::DynamicArray<T> a0_; // empty
structs::DynamicArray<T> a1_; // filled upon initialization
T value_;
T array_[TEST_INITIAL_SIZE];
std::vector<T> sample_values_;
};
} // namespace test
} // namespace structs
using namespace structs::test;
template <typename T>
void DynamicArrayTest<T>::SetUp()
{
}
template<>
void DynamicArrayTest<std::string>::SetUp()
{
value_ = generate_string(generate_numeric<int>(0, 20));
for (size_t i = 0; i < TEST_INITIAL_SIZE; i++)
array_[i] = generate_string(generate_numeric<int>(0, 20));
for (size_t i = 0; i < TEST_INITIAL_SIZE; i++)
sample_values_.push_back(generate_string(generate_numeric<int>(0, 20)));
for (size_t i = 0; i < TEST_INITIAL_SIZE; i++)
a1_.push_back(generate_string(generate_numeric<int>(0, 20)));
}
template <typename T>
void DynamicArrayTest<T>::TearDown()
{
// clean up
a1_.reset();
}
The functions that generate the random values lives in tests/TestHelpers/generators.h:
template<typename T>
T generate_numeric(const T, const T);
char generate_char();
std::string generate_string(const size_t);
Test cases: DynamicArrayTest.cpp
#include <iostream>
#include "gtest/gtest.h"
#include "structs/structures.h"
#include "TestClasses/DynamicArrayTest.h"
#include "TestHelpers/generators.h"
using namespace structs::test;
TYPED_TEST_SUITE_P(DynamicArrayTest);
TYPED_TEST_P(DynamicArrayTest, AddMethodTest)
{
//structs::DynamicArray<TypeParam> initial_a1 = this->a1_;
size_t random_index = generate_numeric(0, TEST_INITIAL_SIZE);
size_t initial_size = this->a1_.size();
this->a1_.add(random_index, this->value_);
// Value is added at correct index
EXPECT_EQ(this->a1_.get(random_index), this->value_);
// size is updated
EXPECT_EQ(this->a1_.size(), initial_size + 1);
}
REGISTER_TYPED_TEST_SUITE_P(DynamicArrayTest,
AddMethodTest
);
//using MyTypes = ::testing::Types<char, int, char*, int*, std::string> ;
using MyTypes = ::testing::Types<std::string> ;
INSTANTIATE_TYPED_TEST_SUITE_P(DynamicArrayTest_, DynamicArrayTest, MyTypes);
Output Samples:
$ ./StructsTest
[==========] Running 13 tests from 1 test suite.
[----------] Global test environment set-up.
[----------] 13 tests from DynamicArrayTest_/DynamicArrayTest/0, where TypeParam = std::basic_string<char, std::char_traits<char>, std::allocator<char> >
[ RUN ] DynamicArrayTest_/DynamicArrayTest/0.AddMethodTest
[ OK ] DynamicArrayTest_/DynamicArrayTest/0.AddMethodTest (0 ms)
[----------] 13 tests from DynamicArrayTest_/DynamicArrayTest/0 (8 ms total)
[----------] Global test environment tear-down
[==========] 13 tests from 1 test suite ran. (8 ms total)
[ PASSED ] 12 tests.
$ ./StructsTest
[==========] Running 13 tests from 1 test suite.
[----------] Global test environment set-up.
[----------] 13 tests from DynamicArrayTest_/DynamicArrayTest/0, where TypeParam = std::basic_string<char, std::char_traits<char>, std::allocator<char> >
[ RUN ] DynamicArrayTest_/DynamicArrayTest/0.AddMethodTest
Segmentation fault (core dumped)
When running the test with valgrind and a seg fault happens, valgrind always show this:
==138433== Process terminating with default action of signal 11 (SIGSEGV)
==138433== Access not within mapped region at address 0x0
==138433== at 0x49F1241: std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_assign(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.28)
==138433== by 0x49F15FD: std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::operator=(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.28)
==138433== by 0x127B84: structs::DynamicArray<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >::add(unsigned long, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >) (DynamicArray.h:160)
==138433== by 0x127336: gtest_suite_DynamicArrayTest_::AddMethodTest<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >::TestBody() (DynamicArrayTest.cpp:84)
The line where the error happens as pointed by valgrind is here:
// DynamicArray.h
template<typename T>
void structs::DynamicArray<T>::add( size_t index, T value )
{
if (index > m_size)
throw std::out_of_range("Invalid: Exceeds range, use set(size_t, T) instead");
if ( m_size == m_capacity )
resize();
for (long i = m_size - 1; i >= index; i--)
m_data[i+1] = m_data[i]; // <- this
m_data[index] = value;
m_size ++;
}
If the issue is with the assignment, I don't understand why does it succeed sometimes?
The issue was in this line:
size_t random_index = generate_numeric(0, TEST_INITIAL_SIZE);
TEST_INITIAL_SIZE was set to 10 which is the max capacity size causing resize() to be called and it wasn't implemented properly.
I modified the constant to a value less than max capacity by 2
#define TEST_INITIAL_SIZE INITIAL_SIZE - 2
solved

Clang/LLVM 9 and 10 SIGSEGV for inline static class members. Bug?

Using inline static class members in Clang gives me unexpected behaviour when the member is another class/struct:
https://godbolt.org/z/mbH6k7
// std=c++17
#include <iostream>
struct A {
double a = 42;
A() { std::cout << "A()" << std::endl; }
};
inline static A a{}; // No problem
namespace N {
inline static A a{}; // No problem
}
struct B {
B() { std::cout << "B()" << std::endl; }
inline static double d; // No problem with built-in types
A& a1 = N::a; // No problem
inline static A a2 = N::a; // No problem
inline static A a3{}; // <-- Problem here!
};
B b1;
inline static B b2;
int main() {
return 0;
}
Expected output, works in Clang 8.0.0, gcc, msvc:
A()
A()
A()
B()
B()
Actual output for Clang 9.0.0 and onwards: 139 (SIGSEGV).
Is this a bug, or what am I missing?
This sounds like a pretty cut-and-dry bug to me:
Per [class.static.data]
An
inline static data member may be defined in the class definition and may specify a brace-or-equal-initializer.
Your code conforms to this:
struct B {
// ...
inline static A a3{};
};

C Macro for type safe callbacks

I'm trying to create a very simple event system in c. My interface looks like this:
typedef struct EventEmitter EventEmitter;
EventEmitter* emitter_create();
void emitter_subscribe(EventEmitter* emitter, void (*cb)(void*));
void emitter_publish(EventEmitter* emitter, void *payload);
Everything works correctly, but in order to register an event listener, I need to provide a function pointer that takes a void *.
static void registerOnKeyDown(void (*cb)(void*)) {
emitter_subscribe(keyDownEmitter, cb);
}
static void registerOnKeyUp(void (*cb)(void*)) {
emitter_subscribe(keyUpEmitter, cb);
}
Is there any way, using a macro or otherwise, to allow users of EventEmitters to provide a typed callback? Something like:
void onKey(int keyCode) {
printf("%d", keyCode);
}
instead of:
void onKey(void *keyCode) {
int code = (int)keyCode;
printf("%d", code);
}
I ended up solving this by simply casting to and from void (*cb)(void *) as needed in wrapper functions:
typedef void (*keyCallback)(int);
typedef void (*emitterCallback)(void*);
static void registerOnKeyDown(keyCallback cb) {
emitter_subscribe(keyDownEmitter, (emitterCallback)cb);
}
If you know what your types are, you can use C11 generic selection to find out the type of the argument, and provide it as an enum value.
#include <stdio.h>
typedef struct EventEmitter EventEmitter;
typedef void (*INT_CALLBACK)(int);
typedef void (*VOIDP_CALLBACK)(void *);
enum cbtype {
_INT_CB,
_VOIDP_CB
};
void _safe_emitter_subscribe(EventEmitter *emitter,
void (*callback)(),
enum cbtype type)
{
printf("Registering a callback of type %d\n", type);
}
#define safe_emitter_subscribe(emitter, callback) \
_safe_emitter_subscribe( \
emitter, \
(void (*)())callback, \
_Generic(callback, \
INT_CALLBACK: _INT_CB, \
VOIDP_CALLBACK: _VOIDP_CB))
void func1(int a) {
}
void func2(void *a) {
}
int main(void) {
safe_emitter_subscribe(NULL, func1);
safe_emitter_subscribe(NULL, func2);
}
Then from the enum value you will know how you'd need to cast the function again: If it is _INT_CB it must be recast as INT_CALLBACK before calling; _VOIDP_CB as VOIDP_CALLBACK and so on.
See this answer on Software Engineering SE.
Given your API:
typedef struct EventEmitter EventEmitter;
EventEmitter* emitter_create();
void emitter_subscribe(EventEmitter* emitter, void (*cb)(void*));
void emitter_publish(EventEmitter* emitter, void *payload);
if you modify it to define the subscription macro on that API instead of putting off on the client code, like this:
typedef struct EventEmitter EventEmitter;
EventEmitter* emitter_create();
void emitter_subscribe_impl(EventEmitter* emitter, void (*cb)(void*));
#define emitter_subscribe(emitter, xFunc) emitter_subscribe_impl((emitter), (void(*)(void*))(xFunc))
void emitter_publish_impl(EventEmitter* emitter, void *payload);
#define emitter_publish(emitter, xFunc) emitter_publish_impl((emitter), (void(*)(void*)(xFunc))
Then subscribers can call it with the type that they have in hand. As with all API macros, make sure you document the expected arguements completely, so consumers know what to provide and what to expect.

Rewrite GCC cleanup macro with nested function for Clang?

I'm trying to work through an issue on a third party library. The issue is the library uses GCC's nested functions buried in a macro, and Clang does not support nested functions and has no plans to do so (cf., Clang Bug 6378 - error: illegal storage class on function).
Here's the macro that's the pain point for me and Clang:
#define RAII_VAR(vartype, varname, initval, dtor) \
/* Prototype needed due to http://gcc.gnu.org/bugzilla/show_bug.cgi?id=36774 */ \
auto void _dtor_ ## varname (vartype * v); \
void _dtor_ ## varname (vartype * v) { dtor(*v); } \
vartype varname __attribute__((cleanup(_dtor_ ## varname))) = (initval)
And here's how its used (from the code comments):
* void do_stuff(const char *name)
* {
* RAII_VAR(struct mything *, thing, find_mything(name), ao2_cleanup);
* if (!thing) {
* return;
* }
* if (error) {
* return;
* }
* do_stuff_with_thing(thing);
* }
The Clang User Manual states to use C++ and a lambda function to emulate. I'm not sure that's the best strategy, and a C project will likely not accept a C++ patch (they would probably tar and feather me first).
Is there a way to rewrite the macro so that's its (1) more accommodating to Clang, and (2) preserves original function semantics?
Clang doesn't support GCC nested functions, but it does support Objective C-style "blocks", even in C mode:
void f(void * d) {
void (^g)(void *) = ^(void * d){ };
g(d);
}
You need to invoke it with the clang command rather than gcc, and also (?) pass -fblocks -lBlocksRuntime to the compiler.
You can't use a block as a cleanup value directly, since it has to be a function name, so (stealing ideas from here) you need to add a layer of indirection. Define a single function to clean up void blocks, and make your RAII'd variable the block that you want to run at the end of the scope:
typedef void (^cleanup_block)(void);
static inline void do_cleanup(cleanup_block * b) { (*b)(); }
void do_stuff(const char *name) {
cleanup_block __attribute__((cleanup(do_cleanup))) __b = ^{ };
}
Because blocks form closures, you can then place the operations on your variables to cleanup directly inside that block...
void do_stuff(const char *name) {
struct mything * thing;
cleanup_block __attribute__((cleanup(do_cleanup))) __b = ^{ ao2_cleanup(thing); };
}
...and that should run at the end of the scope as before, being invoked by the cleanup on the block. Rearrange the macro and add a __LINE__ so it works with multiple declarations:
#define CAT(A, B) CAT_(A, B)
#define CAT_(A, B) A##B
#define RAII_VAR(vartype, varname, initval, dtor) \
vartype varname = (initval); \
cleanup_block __attribute__((cleanup(do_cleanup))) CAT(__b_, __LINE__) = ^{ dtor(varname); };
void do_stuff(const char *name) {
RAII_VAR(struct mything *, thing, NULL, ao2_cleanup);
...
Something like that, anyway.
I believe you can do this without using a clang-specific version, I'd try something like this (untested, may require a few extra casts):
struct __destructor_data {
void (*func)(void *);
void **data;
}
static inline __destructor(struct __destructor_data *data)
{
data->func(*data->data);
}
#define RAII_VAR(vartype, varname, initval, dtor) \
vartype varname = initval; \
__attribute((cleanup(__destructor))) \
struct __destructor_data __dd ## varname = \
{ dtor, &varname };
In our project we have a gcc-specific _auto_(dtor) macro that precedes the normal variable declaration, e.g.:
_auto_(free) char *str = strdup("hello");
In this case our macro can't add anything after the variable declaration and also doesn't know the name of the variable, so to avoid using gcc-specific nested functions I came up with the following hackish version in case this helps anyone:
static void *__autodestruct_value = NULL;
static void (*__autodestruct_dtor)(void *) = NULL;
static inline void __autodestruct_save_dtor(void **dtor)
{
__autodestruct_dtor = *dtor;
__autodestruct_dtor(__autodestruct_value);
}
static inline void __autodestruct_save_value(void *data)
{
__autodestruct_value = *(void **) data;
}
#define __AUTODESTRUCT(var, func) \
__attribute((cleanup(__autodestruct_save_dtor))) \
void *__dtor ## var = (void (*)(void *))(func); \
__attribute((cleanup(__autodestruct_save_value)))
#define _AUTODESTRUCT(var, func) \
__AUTODESTRUCT(var, func)
#define _auto_(func) \
_AUTODESTRUCT(__COUNTER__, func)
This is hackish because it depends on the order the destructors are called by the compiler being the reverse of the order of the declarations, and it has a few obvious downsides compared to the gcc-specific version but it works with both compilers.
Building on the answers above, here's my hack to allow clang to compile nested procedures written in gcc-extension style. I needed this myself to support a source-to-source translator for an Algol-like language (Imp) which makes heavy use of nested procedures.
#if defined(__clang__)
#define _np(name, args) (^name)args = ^args
#define auto
#elif defined(__GNUC__)
#define _np(name, args) name args
#else
#error Nested functions not supported
#endif
int divide(int a, int b) {
#define replace(args...) _np(replace, (args))
auto int replace(int x, int y, int z) {
#undef replace
if (x == y) return z; else return x;
};
return a / replace(b,0,1);
}
int main(int argc, char **argv) {
int a = 6, b = 0;
fprintf(stderr, "a / b = %d\n", divide(a, b));
return 0;
}

C programming, make change at two location with one macro

struct Error
{
MACRO(1, Connect);
MACRO(2, Timeout);
};
I need to define MACRO() in such way that the above code will generate the following code.
struct Error
{
static const int Connect = 1;
static const int Timeout = 2;
const char * const name[] = {"Connect", "Timeout"};
};
Is this possible or what is the alternative to get what I'm trying to do?
You can't do this directly, but you can if you move the macros to a separate location (such as a separate file):
macros.hpp
MACRO(1, Connect)
MACRO(2, Timeout)
#undef MACRO
the other file
struct Error
{
#define MACRO(a, b) static const int b = a;
#include "macros.hpp"
const char * const name [] = {
#define MACRO(a, b) #b,
#include "macros.hpp"
}
};
Alternatively, you could achieve a similar effect with Boost.Preprocessor.
Here's a Boost.Preprocessor solution:
#include <boost/preprocessor/seq/for_each.hpp>
#include <boost/preprocessor/seq/size.hpp>
#include <boost/preprocessor/tuple/elem.hpp>
#include <boost/preprocessor/stringize.hpp>
#define FIRST(a, b) a
#define SECOND(a, b) b
#define DECLARE_VAR(r, data, elem) \
static const int FIRST elem = SECOND elem;
#define NAME_ARRAY_ELEM(r, data, elem) \
BOOST_PP_STRINGIZE(FIRST elem),
#define MACRO(seq) \
BOOST_PP_SEQ_FOR_EACH(DECLARE_VAR, ~, seq) \
const char * const name[] = { \
BOOST_PP_SEQ_FOR_EACH(NAME_ARRAY_ELEM, ~, seq) \
}
int main()
{
MACRO(((Connect, 1))((TimeOut, 2)));
return 0;
}
You have to make sure to double bracket each ((Token, value)) pair, however you don't need a separate file for your macro.
What you want, is to have a single list, that will automatically generate the definition and the name list, correct?
If so, search for X Macros in google.
Example:
#define EXPAND_AS_DEFINITION(a, b) static const int b = a;
#define EXPAND_AS_ARRAY(a, b) #b,
#define STATE_TABLE(ENTRY) \
ENTRY(1, Connect) \
ENTRY(2, Timeout)
struct Error
{
STATE_TABLE(EXPAND_AS_DEFINITION)
static const char * const name[];
};
const char * const Error::name[] = {STATE_TABLE(EXPAND_AS_ARRAY) 0};
It looks like like you are trying to define an enum Error that also has the strings as members. I will give you my own solution to this problem. (I'm not addressing the question but I believe that my answer is relevant for what I understand that OP is trying to do.)
And I just realized that OP is targeting C, not C++, so not sure if this can be done...
In MyEnum.hpp
#define MYENUM(X,...) \
struct X { \
enum Enum {__VA_ARGS__}; \
static const std::vector<std::string> names; \
static X::Enum which(const std::string& s) { \
return static_cast<X::Enum>(findEnum(s,names)); \
} \
static std::string str(X::Enum i) { \
return names[i];} \
}
Here findEnum() is just a linear search over the vector that returns the position index (additionally, in my implementation if it doesn't find it it throws an exception with all the possible correct inputs, I also do case insensitive comparison). Note that an ordered map instead of a vector would be more efficient (O(log(n)) instead of O(n)), but I didn't cared much because the size of those things is very small in my case.
Below the previous macro, declare your enum as
MYENUM(Error,Connect,Timeout); // I put the semicolon here not in the macro
And in MyEnum.cpp, add
#include <boost/assign/list_of.hpp>
const std::vector<std::string> Error::names = boost::assign::list_of
("Connect")("Timeout");
(I think that it should be possible to use initialization lists with a modern compiler). The important thing here is to make sure that the order is the same, otherwise it will not work.
Then, you can do stuff like this:
Error::Enum err1 = Error::Connect;
Error::Enum err2 = Error::which("Timeout");
std::cout << "Got " << Error::str(err1) << " error. Not good.\n";

Resources