I have third party C library. I want to use it in c++
Every function return error code.
when i have to use it I have to write code like this:
int err;
err=libFun1(....);
check(err);
err=libFun2(....);
check(err);
I want to wrap this functions and avoid code duplication of check. Every of this librabry function have different number parameters. What will be a good design for this?
How about a templated function:
template <typename T>
int CallWrapper(T func)
{
int error = func();
check(error);
return error;
}
Then call it with CallWrapper(libFun1);.
/edit 4: The C++11-Way using variadic templates, inspired by Gill Bates' solution:
template <typename T, class ...A> int CallWrapper(T func, A... args) {
int error = func(args...);
check(error);
return error;
}
CallWrapper(libFun1);
CallWrapper(libFun2, 4711);
CallWrapper(libFun3, 42, 23);
/edit 5: older solutions, beginning with first solution:
#define LIBFUN1() do { \
int err = libFun1(); \
check(err); \
} while (0)
#define LIBFUN2() do { \
int err = libFun2(); \
check(err); \
} while (0)
LIBFUN1();
LIBFUN2();
Put the #defines in some header file. Please note the MISSING semicolon after the while (). This way, you can naively use LIBFUN1() and so on in any context, where a statement is allowed like if (...) LIBFUN1(); else LIBFUN2();
/edit 3: Instead of using #defines, static inline functions would do the job too:
static inline int checked_libFun1() {
int err = libFun1();
check(err);
return err;
}
static inline int checked_libFun2() {
int err = libFun2();
check(err);
return err;
}
checked_libFun1();
checked_libFun2();
/edit 2: #Myst suggested to use variadic macros that contain the name of the function to call. This could look like this:
#define call_void(name) do { \
int err = name(); \
check(err); \
} while (0)
#define call_args(name, ...) do { \
int err = name(__VA_ARGS__); \
check(err); \
} while (0)
call_void(libFun1);
call_args(libFun2, 42);
The two macros are needed because you have to distinguish between functions not accepting any arguments and functions accepting any number of arguments greater one. So, here, libFun2(42) would be called
You can use exceptions and catch them.
One way is to use Macros like:
#include <exception>
#define THROW_ON_ERROR(libFunc) do{ \
int err = libFunc(); \
if(check(err)) \
throw std::exception(); \
}while(0); \
and you code will look like this:
try
{
THROW_ON_ERROR(libFun1);
THROW_ON_ERROR(libFun2);
}catch(const std::exception& e)
{
//handle...
}
This method is not very modern but does the job. It was just to pass the point of converting the error status code convention which is the common way in C to exception handling which is a nice way in C++ (not sure if common).
Also you can use your own exceptions to pass some data.
You can do the same thing by calling a function like:
#include <functional>
class LibFuncException : public std::exception
{
public:
explicit LibFuncException(int err) : m_err(err) {}
int GetError() const { return m_err; }
private:
int m_err;
};
void ThrowOnError(std::function<int()> libFunc)
{
int err = libFunc();
if(check(err)
throw LibFuncException(err);
}
and your code can:
try{
ThrowOnError(libFunc1);
ThrowOnError(libFunc2);
} catch(const LibFuncException& e)
{
std::cout << "Error: << e.GetError() << std::endl;
}
EDIT:
if your library function sometime receive arguments you can either call the ThrowOnError with a lambda like so:
int x = 10;
const char* str = "Hello World";
ThrowOnError([x, str]() { return libFuncWithArgs(x, str); });
Or if you want to be extreme you can have a variadic template like someone suggested already
From given code, you may simply write
check(libFun1(/*...*/));
check(libFun2(/*...*/));
Possibly, you may want to wrap each method to have only one call from user side:
void chekedLibFun1(/*...*/) { check(libFun1(/*...*/)); }
void chekedLibFun2(/*...*/) { check(libFun2(/*...*/)); }
and so previous code becomes:
checkedLibFun1(/*...*/);
checkedLibFun2(/*...*/);
I can see a few ways to do this:
Template functions (#GillBates has you covered).
An inline function (maybe with variadic arguments).
Simple macros (#usr and #ZivS have you covered).
A variadic Macro.
Since you're writing for C++, I'd probably use Template functions... the only thing negative about that option is that function argument handling (assuming the library functions accept arguments) could be a headache.
I think the C approach of a variadic Macro would work better, also providing compile-time unwrapping (vs. runtime unwrapping).
i.e. (this is only an example, I didn't even test the code)
int checkerr(int err) {
if(!err)
return 0;
// ... handle errors, exit if need be.
}
#define CALL_CLIB(fn_name, ...) checkerr((fn_name)(__VA_ARGS__))
You can use it like so:
// will print "this is only 1 example"
CALL_CLIB(printf, "this is only %d, %s", 1, "example");
You can make it also more complex, if you want, adding try, catch or whatever you want into the mix.
Related
I'm currently working on a cpp logger which aims at displaying the __FILE__ and the __LINE__ before each printed message. In my case, we are mostly using 2 methods for printing out: printf-style and std::cout-style. For the moment I have a macros for each style:
#define HATFormatFatal(...) HATLogger::logFormat(HATLogger::LogLevel::FATAL, __FILE__, __LINE__, __VA_ARGS__)
#define HATFormatError(...) HATLogger::logFormat(HATLogger::LogLevel::ERROR, __FILE__, __LINE__, __VA_ARGS__)
etc... and:
#define HATStreamFatal HATLogger::logStream(HATLogger::LogLevel::FATAL, __FILE__, __LINE__)
#define HATStreamError HATLogger::logStream(HATLogger::LogLevel::ERROR, __FILE__, __LINE__)
These macros can be called in the following:
HATFormatError("This is an %s message", "ERROR");
HATStreamError << "This is an " << "ERROR" << " message" << std::endl;
I would like to call them with the same name: HATLogError. The right macro would be determine at compilation while looking for parenthesis. So far I've seen some examples showing how it is possible to discriminate the macros by the number of arguments, but nothing that could handle a "non-parenthesis" case.
Does anyone have any idea on how this could be achieved ?
The simplest approach would be not overloading the macro at all, but instead having the macro return an object that has both operator<< and operator() overloaded. Something like this:
class error_logger {
public:
error_logger(
HATLogger::LogLevel level,
char const * file,
char const * line
) : level{level}, file{file}, line{line} { }
template <typename... T>
void operator()(T && ... args) {
HATLogger::logFormat(level, file, line, std::forward<T>(args)...);
}
template <typename T>
HATLogger::logStream operator<<(T && arg) {
HATLogger::logStream stream{level, file, line};
stream << std::forward<T>(arg);
return stream;
}
private:
HATLogger::LogLevel level;
char const * file;
char const * line;
};
(This example assumes HATLogger::logStream can be moved. Adjustments to this example implementation may need to be made based on the details of your code, but the basic approach is what I'm demonstrating here.)
Now you could do:
#define HATFormatFatal (error_logger{HATLogger::LogLevel::FATAL, __FILE__, __LINE__})
And then both HATFormatFatal << ... and HATFormatFatal(...) can be used.
I have some well-formed code looks like this:
NAMESPACE_BEGIN(Foo)
inline void test() {
string s = xxx;
}
NAMESPACE_END(Foo)
So, is there any portable tricks by using the NAMESPACE_BEGIN() macro to obtain namespace name "Foo" in test()?
I'm thinking of something like this, but it would surely cause symbol redefinition:
#define NAMESPACE_BEGIN(x) \
namespace x { \
inline const char *_curNamespace() { \
return #x; \
}
#define NAMESPACE_END(x) \
}
There's also a workaround looks like this, but that's not very convenient
#define NAMESPACE_NAME Foo
// using a header file so that we can use #ifdef guard
#include "MyNamespaceBegin.h"
...
#include "MyNamespaceEnd.h"
EDIT:
Why I need this:
I'm using much of macro to generate codes to achieve some
dynamic reflection logic (yes, not static template reflection),
it's all right within class scope by using static member function,
but does not work for namespaces
Why not to manually declare the name getter once:
What I want is something like this:
// the global default version
const char *_curNamespace() {return "";}
namespace X {
// the local namespace version
const char *_curNamespace() {return "X";}
// some verbose reflection register code
...
registerSomething(_curNamespace());
...
}
Of course, all of the verbose register code should be generated by macro
And, app level user should not care about the _curNamespace(),
so, I want to simplify the user's usage,
by using a custom NAMESPACE_BEGIN(xxx) macro at any case
If you are still curious about what I'm doing,
check this: https://github.com/ZFFramework/ZFFramework
I'm using lots of tricks to achieve fully dynamic reflection in pure C++,
to achieve some of my fancy thoughts,
for now, this project is just for fun,
I have no idea whether it has practicability
EDIT2:
For now, I think the best workaround should be like this:
#define NAMESPACE_BEGIN(ns) \
namespace ns { \
extern const char *__curNS();
#define NAMESPACE_END(ns) \
}
#define NAMESPACE_REG(ns) \
const char *__curNS() {return #ns;}
app level users still only need to care about NAMESPACE_BEGIN
NAMESPACE_REG must be declared exactly once, in source file
if not, undefined symbol would happen
if more than once, duplicated symbol would happen
although it's annoying and sometimes you need additional source file
to hold the NAMESPACE_REG,
the strict rule should prevent user from forgetting the ugly workaround
You are making much fuss over something that is trivial to implement.
First of all, use of NAMESPACE_BEGIN and NAMESPACE_END seems unnecessary to me. I don't see how that is more readable or useful than
namespace Foo
{
}
If getting the name of the namespace is important/useful, add a trivial function.
namespace Foo
{
inline std::string get_name() { return "Foo"; }
}
Small sized real world applications need thousands of lines of code. Large sized real world applications need millions of lines of code. From that perspective, implementing a one line inline function is a very minor task.
This solution employs a bit of preprocessor magic and has these features:
Namespace is mentioned only once
Access to a macro containing the unquoted name
Access to a macro containing the quoted name
Support for repeating the same namespace
Support for different namespaces
Misuse of the BEGIN/END macros is detected
Cleanup, i.e. no extra macros defined outside the BEGIN/END block
It does not support nested namespaces.
Example of usage:
#include "framework.hpp"
#define NAMESPACE_NAME Foo
#include NAMESPACE_BEGIN
// Here you have access to NAMESPACE_NAME (unquoted, i.e. Foo)
// and also to NAMESPACE_NAME_STRING (quoted, i.e. "Foo")
#include NAMESPACE_END
// NAMESPACE_NAME and NAMESPACE_NAME_STRING do not exist
// outside the block, so they cannot be misused
// Different namespaces in the same TU are supported
#define NAMESPACE_NAME Bar
#include NAMESPACE_BEGIN
inline std::string f()
{
return NAMESPACE_NAME_STRING;
}
#include NAMESPACE_END
// Repeating the same namespace is also supported
#define NAMESPACE_NAME Foo
#include NAMESPACE_BEGIN
inline std::string f()
{
return NAMESPACE_NAME_STRING;
}
#include NAMESPACE_END
The implementation follows:
framework.hpp
#pragma once
#define NAMESPACE_BEGIN "framework_namespace_begin.hpp"
#define NAMESPACE_END "framework_namespace_end.hpp"
framework_namespace_begin.hpp
#ifndef NAMESPACE_NAME
#error "NAMESPACE_NAME not defined"
#endif
#define NAMESPACE_IN_NAMESPACE 1
#define NAMESPACE_NAME_DO_STR(X) #X
#define NAMESPACE_NAME_STR(X) NAMESPACE_NAME_DO_STR(X)
#define NAMESPACE_NAME_STRING NAMESPACE_NAME_STR(NAMESPACE_NAME)
namespace NAMESPACE_NAME {
framework_namespace_end.hpp
#ifndef NAMESPACE_IN_NAMESPACE
#error "NAMESPACE_IN_NAMESPACE not defined"
#endif
}
#undef NAMESPACE_NAME
#undef NAMESPACE_NAME_STRING
#undef NAMESPACE_IN_NAMESPACE
You know what? I think I might just have a viable solution for this. It's actually very simple, and it's very close to the OP's original suggestion (which really only had the problem of a potential duplicate definition if you wanted to open the namespace twice in the same translation unit). You just have to think a bit laterally and not be too precious about seeing your namespaces being bracketed by macros instead of curly braces.
So let me just lay it out here, because there's really nothing to it, and then I'll explain why I personally happen to like it.
Code:
Macros:
#define DECLARE_NAMESPACE(ns) \
namespace ns {\
static constexpr const char *_curNamespace = #ns; \
}
#define BEGIN_NAMESPACE(ns) \
namespace ns { \
static_assert (ns::_curNamespace, "BEGIN_NAMESPACE: namespace has not been declared");
#define END_NAMESPACE }
Sample code:
#include <iostream>
DECLARE_NAMESPACE (Foo)
BEGIN_NAMESPACE (Foo)
void print_namespace_name () { std::cout << _curNamespace << "\n"; }
END_NAMESPACE
BEGIN_NAMESPACE (Foo)
void another_print_namespace_name () { std::cout << _curNamespace << "\n"; }
END_NAMESPACE
DECLARE_NAMESPACE (Bar)
BEGIN_NAMESPACE (Bar)
void print_namespace_name () { std::cout << _curNamespace << "\n"; }
DECLARE_NAMESPACE (BarBar)
BEGIN_NAMESPACE (BarBar)
void print_namespace_name () { std::cout << _curNamespace << "\n"; }
END_NAMESPACE
END_NAMESPACE
int main ()
{
Foo::print_namespace_name ();
Foo::another_print_namespace_name ();
Bar::print_namespace_name ();
Bar::BarBar::print_namespace_name ();
}
Output:
Foo
Foo
Bar
BarBar
Now this is obviously very straightforward to implement and also easy to use and has no obvious limitations. In particular, it can handle nested namespaces (as shown in the code above) and opening a namespace twice in the same compilation unit also works (again, this is shown in the code snippet).
But, but, but, don't we still have to type in the name of the namespace twice, and wasn't that the very thing we were trying to avoid to eliminate typos?
Well, sure, we have to type the name in twice, but so what, live with it. Point is, with this particular set of macros, the compiler will now catch any typos for us. Let's prove that by deliberately putting one in. So this:
DECLARE_NAMESPACE Whoops
BEGIN_NAMESPACE whoops
END_NAMESPACE
Generates this (I couldn't find a better way of formulating the static_assert, sorry):
prog.cc:12:24: error: '_curNamespace' is not a member of 'whoops'
static_assert (ns::_curNamespace, "BEGIN_NAMESPACE: namespace has not been declared");
^~~~~~~~~~~~~
prog.cc:27:5: note: in expansion of macro 'BEGIN_NAMESPACE'
BEGIN_NAMESPACE (whoops)
^~~~~~~~~~~~~~~
And more importantly this (and this is why we need the BEGIN_NAMESPACE macro):
DECLARE_NAMESPACE (Bar)
BEGIN_NAMESPACE (Bar)
DECLARE_NAMESPACE (BarWhoops)
BEGIN_NAMESPACE (Barwhoops)
END_NAMESPACE
END_NAMESPACE
Generates this:
prog.cc:12:24: error: '_curNamespace' is not a member of 'Bar::Barwhoops'
static_assert (ns::_curNamespace, "BEGIN_NAMESPACE: namespace has not been declared");
^~~~~~~~~~~~~
prog.cc:42:5: note: in expansion of macro 'BEGIN_NAMESPACE'
BEGIN_NAMESPACE (Barwhoops)
^~~~~~~~~~~~~~~
Which is just dandy.
So, you know, what's not to like?
Live demo - uncomment line 3 to see those compiler errors.
you can use a variable and change its value with 'NAMESPACE_BEGIN' and 'NAMESPACE_END'
the variable __name represent the current full namespace position
like "abc::def::detail"
std::string __name = "";
std::string & __append(std::string & str, const char * ptr) {
if (!str.empty()) {
str.append("::");
}
str.append(ptr);
return str;
}
std::string & __substr(std::string & str, const char * ptr) {
if (str.length() == strlen(ptr)) {
str.clear();
} else {
str = str.substr(0, str.length() - strlen(ptr) - 2);
}
return str;
}
#define NAMESPACE_NAME __name
#define CONCATENATE_DIRECT(s1, s2) s1##s2
#define CONCATENATE(s1, s2) CONCATENATE_DIRECT(s1, s2)
#ifdef _MSC_VER
# define ANONYMOUS_VARIABLE(str) CONCATENATE(str, __COUNTER__)
#else
# define ANONYMOUS_VARIABLE(str) CONCATENATE(str, __LINE__)
#endif
#define APPEND_NAME(x) std::string ANONYMOUS_VARIABLE(__start_name) = __append(__name, #x)
#define SUBSTR_NAME(x) std::string ANONYMOUS_VARIABLE(__end_name) = __substr(__name, #x)
#define NAMESPACE_BEGIN(x) \
namespace x { \
APPEND_NAME(x);
#define NAMESPACE_END(x) \
SUBSTR_NAME(x); \
}
then you can use the NAMESPACE_NAME macro for the full name or you can extract the last name from it
Here's a way. The core idea came from the line of thought:
Q: How can I define multiple things with the same name accessible from the same scope?
A: Make them all functions with different parameter types. And if all of them have identical bodies, it doesn't matter which one gets called.
Q: How can I generate an unlimited set of different parameter types?
A: A class template.
Q: How can I make sure a call to that set of overloaded functions will never be ambiguous?
A: Make sure the binary relation "is implicitly convertible from" is a complete ordering on the parameter types, and use a unique minimal element for the argument type.
#include <type_traits>
#include <functional>
struct NamespaceHandleObj {
template <const NamespaceHandleObj* Handle1, const NamespaceHandleObj* Handle2>
struct less : public std::bool_constant<std::less<>{}(Handle1, Handle2)> {};
};
template <>
struct NamespaceHandleObj::less<nullptr, nullptr> : public std::false_type {};
template <const NamespaceHandleObj* Handle1>
struct NamespaceHandleObj::less<Handle1, nullptr> : public std::false_type {};
template <const NamespaceHandleObj* Handle2>
struct NamespaceHandleObj::less<nullptr, Handle2> : public std::true_type {};
template <const NamespaceHandleObj* Handle>
struct NamespaceParamType
{
constexpr NamespaceParamType() noexcept = default;
template <const NamespaceHandleObj* Other,
typename = std::enable_if_t<NamespaceHandleObj::less<Other, Handle>::value>>
constexpr NamespaceParamType(NamespaceParamType<Other>) noexcept {}
};
#define NAMESPACE_UTILS_TOSTR1(x) #x
#define NAMESPACE_UTILS_TOSTR(x) NAMESPACE_UTILS_TOSTR1(x)
#define BEGIN_NAMESPACE(ns) \
namespace ns { \
namespace { \
constexpr NamespaceHandleObj namespace_handle_{}; \
constexpr const char* current_ns_(
NamespaceParamType<&namespace_handle_>) noexcept \
{ return NAMESPACE_UTILS_TOSTR(ns); } \
}
#define END_NAMESPACE }
#define CURRENT_NAMESPACE (current_ns_(NamespaceParamType<nullptr>{}))
The code above is C++17, but it wouldn't be hard to port it to previous versions, even all the way to C++03.
I have a class in my C++ code similar to the following:
class myClass
{
public:
void method1(int a, int b, std::string str);
};
Other classes can instantiate an object from myClass and call method1.
void caller()
{
obj1->method1(12, 4, "sample");
}
I want to log all the callers of myClass (function name, file name, line number). One possible solution is this:
class myClass
{
public:
method1(int a, int b, std::string str, const char *_function = __builtin_FUNCTION(), const char *_file = __builtin_FILE(), int _line = __builtin_LINE());
};
which is using __builtin_xxx as default arguments. This solution has multiple shortcomings:
It is an ugly solution
__builtin_xxx is only available in gcc version > 4.8
We have to add three default parameters to method1
IDE shows the default parameters on auto-completion that are not meant to be provided by the user!
Another solution is using __LINE__, __FILE__ and __func__ that is basically very similar to the previous solution. They are not defined outside of function scope, and they should be used like this:
void caller()
{
obj1->method1(12, 4, "sample", __func__, __FILE__, __LINE__);
}
Here is a working example for both solutions.
Is there any better solution to log the caller when the user calls method1 on myClass object. By a better solution I specifically mean not to change the method1's declaration by adding three more parameters!
Another ugly solution, but I'm using...
Use macros to automatically add __LINE__ __FILE__ ...etc. things into parameters.
For example
#define Method(param0,param1) Method(param0,param1,__LINE__)
It has a lot of problem, if you want macros work as normal function, you has to do a lot of things, and it still may not works.
I use it to help me log errors.
Looks like a duplicate of Print the file name, line number and function name of a calling function - C Prog
I'd pass the data to the function through parameters (maybe get the help of a macro)
int info(const char *fname, int lineno, const char *fxname, ...) { /* ... */ }
int debug(const char *fname, int lineno, const char *fxname, ...) { /* ... */ }
int error(const char *fname, int lineno, const char *fxname, ...) { /* ... */ }
And to call them
info(__FILE__, __LINE__, __func__, ...);
debug(__FILE__, __LINE__, __func__, ...);
error(__FILE__, __LINE__, __func__, ...);
Note: __func__ is C99; gcc, in mode C89 has __FUNCTION__
Let's say I have a template function that takes an integer and a const reference to an instance of type T. Now depending on the integer, only some T's are acceptible, otherwise an exception is thrown at runtime.
If all uses of this function would use constant integers, it would be possible to make the int a template parameter and use a static assertion to check if it is acceptable. So instead of func(1,c) one would use func<1>(c) and would gain compile-time type checking. Is there any way to write func(1,c) and still keep the compile-time check, while also being able to write func(i,c) and use a dynamic assertion? The goal is to make it transparent to the developer. It would simply be great to add this safety without bothering the developers about things like compile-time constants. They'd probably only remember that func(1,c) always works and use that, avoiding the check.
How can I define a function with a static assertion whenever possible and a dynamic assertion otherwise?
The following code shows the solution for GCC by Ivan Shcherbakov:
#include <iostream>
#include <cassert>
template<typename T>
void __attribute__((always_inline)) func(const int& i, const T& t);
void compile_time_error_() __attribute__((__error__ ("assertion failed")));
template<>
void __attribute__((always_inline))
func(const int& i, const float& t)
{
do {
if (i != 0) {
if (__builtin_constant_p(i)) compile_time_error_();
std::cerr << "assertion xzy failed" << std::endl;
exit(1);
}
} while (0);
func_impl<float>(i,t);
}
This will only allow the combination of i=0 and T=float. For other combinations a good way would be creating a Macro that produces the code of template<> func(const int& i, const T& t) with T and i != 0 replaced.
Well, if you're using GCC, you can use a dirty hack, but it will only work when function inlining is enabled (-O1 or more):
void my_error() __attribute__((__error__ ("Your message here")));
template <typename T1, typename T2> struct compare_types
{
enum {Equals = 0};
};
template <typename T1> struct compare_types<T1,T1>
{
enum {Equals = 1};
};
template <typename Type> __attribute__((always_inline)) void func(int a, Type &x)
{
if (__builtin_constant_p(a))
{
if (a == 1 && compare_types<Type,char>::Equals)
my_error();
}
}
In this case when a == 1 and Type is char, you'll get an error. Here's an example that will trigger it:
int main()
{
char x;
func(1, x);
return 0;
}
Note that this example heavily relies on the gcc-specific __builtin_constant_p() function and won't work with other compilers!
Let me paraphrase question to be more accurate in my answer:
Can runtime assert sometimes report errors on compile time.
Gcc can, but only on some optimization level and error message is
very uninformative. Clang itself can't (no error attribute), but do
not forget about clang analyzer.
Analyzer can report some runtime errors like dereferencing null pointer.
So here is an idea and simple test of 'smart' runtime assert:
#include <cstdlib> // std::abort
#if !defined(__clang__) && defined(__GNUC__)
// clang emulates gcc
# define GCC_COMPILER 1
#else
# define GCC_COMPILER 0
#endif
#if GCC_COMPILER
void assertion_failed_message() __attribute__((__error__("assertion failed")));
#endif
inline void smart_assert(bool condition) {
#if GCC_COMPILER
// gcc is very 'sensitive', it must be first code lines in this function
if (__builtin_constant_p(condition) && !condition) {
assertion_failed_message();
}
#endif
if (condition) {
// Ok
return;
}
#if defined(__clang_analyzer__)
enum {
ASSERTION_FAILED = 0xdeadfull
};
int *a = nullptr;
*a = ASSERTION_FAILED;
#endif
// Implement some standart error, like abort
std::abort();
}
void test_condition_2(bool condition) {
smart_assert(condition);
}
void test_condition_1(bool condition) {
test_condition_2(condition);
}
void test_condition_0(bool condition) {
test_condition_1(condition);
}
int main() {
test_condition_0(0==1);
return EXIT_SUCCESS;
}
Gcc report error at O2 optimization level, it's good. But report message
is in main function, and don't leave any information
about test_condition_{0,1,2}.
Clang analyzer report error and if you use Xcode, you can see all path
from main to smart_assert:
P.S. clang analyzer is not perfect, so if you try test_condition_0(argc),
no error will be reported (truely runtime check), but if you try
test_condition_0(argc==1), false positive will be reported.
So, I implemented an enumToString function for several enums that I use a lot (often asked in SO: Is there a simple way to convert C++ enum to string?, Easy way to use variables of enum types as string in C?, ...).
This makes the error messages WAY easier to debug, but I have to maintain the function to add the values that have no string description sometimes.
My code looks like this:
typedef std::map<my_enum_e, const char *> enum_map_t;
static bool s_enum_map_initialized = false;
static enum_map_t s_enum_strings;
static void s_init_maps()
{
#define ADD_ENUM( X ) s_enum_strings[X] = #X;
if( s_enum_strings.size() == 0)
{
ADD_CLASS( MY_ENUM_1 );
ADD_CLASS( MY_ENUM_2 );
/* ... all enums */
}
s_enum_map_initialized = true;
}
const char *Tools::enumCString( my_enum_e e )
{
if( ! s_enum_map_initialized )
{
s_init_maps();
}
// todo: use the iterator instead of searching twice
if( s_enum_strings.find(e) != s_enum_strings.end() )
{
return s_class_strings[e];
}
return "(unknown enum_e)";
}
Now, what I want, is that when I don't find the enum in the map, to return "(unknown enum %d)", e . Which will give me the value of the enum I missed.
This way, even if I didn't add it to the map, I still have its value and I can debug my program.
I can't find a way to do that simply: a stringstream instanciated on the stack will be destroyed right after the return, a static stringstream is not thread-safe, ...
edit: of course, using a std::string as return type would allow me to format it, but I call these functions very often in my code, I figured passing a const char * pointer is faster, since I don't have to push the std::string onto the stack each time.
Any solution?
Return a std::string rather than a char*.
This would allow you to use a std::stringstream to generate your message. The calling site would then just have to use the .c_str( ) member function on std::string to get the C-style pointer (if required).
Personally, I use BOOST :)
Example of use:
SANDBOX_DEFINE_ENUM(MyEnum, (Foo)(Bar)(Team))
Will yield:
struct MyEnum {
enum Type {
Foo,
Bar,
Team
};
static Type const First = Foo;
static Type const Last = Team;
};
inline char const* toString(MyEnum::Type value) {
switch(value) {
case MyEnum::Foo: return "Foo";
case MyEnum::Bar: return "Bar";
case MyEnum::Team: return "Team";
}
return 0;
}
Code:
#include <boost/preprocessor/cat.hpp>
#include <boost/preprocessor/stringize.hpp>
#include <boost/preprocessor/seq/enum.hpp>
#include <boost/preprocessor/seq/for_each.hpp>
#include <boost/preprocessor/seq/reverse.hpp>
#include <boost/preprocessor/seq/seq.hpp>
#define SANDBOX_DEFINE_ENUM(Name_, Values_) \
SANDBOX_DEFINE_ENUM_TYPE(Name_, Values_) \
SANDBOX_DEFINE_ENUM_STRING(Name_, Values_)
#define SANDBOX_DEFINE_ENUM_TYPE(Name_, Values_) \
struct Name_ { \
enum Type { \
BOOST_PP_SEQ_ENUM(Values_) \
}; \
static Type const First = BOOST_PP_SEQ_HEAD(Values_); \
static Type const Last = BOOST_PP_SEQ_HEAD(BOOST_PP_SEQ_REVERSE(Values_)); \
};
#define SANDBOX_DEFINE_ENUM_STRING(Name_, Values_) \
inline char const* toString(Name_::Type value) { \
switch(value) { \
BOOST_PP_SEQ_FOR_EACH(SANDBOX_DEFINE_ENUM_TO_STRING_C, Name_, Values_) \
} \
return 0; \
}
#define SANDBOX_DEFINE_ENUM_TO_STRING_C(r, Name_, Elem_) \
case Name_::Elem_: return BOOST_PP_STRINGIZE(Elem_);
Obviously, it only work with "regular" enums, not with custom made ones. But because it's defined in a single place in the code... no maintenance penalty :)
Try defining a thread-local static variable http://gcc.gnu.org/onlinedocs/gcc-3.3.1/gcc/Thread-Local.html
I wouldn't return a value in this case. I would throw an exception and have your message contain the value of your invalid enumerator. To me, an invalid enumerator value would seem to be an error.
If you don't want to do that, then I agree with others that you should be returning a std::string instead.