#ifndef MACROS_H
#define MACROS_H
//
// Various common macros
//
#define YQ_ARRAY_SIZE(x) (sizeof(x)/sizeof((x)[0]))
#define YQ_EXPAND(x) x
#define YQ_STRINGIZE_IMPL(val) #val
#define YQ_STRINGIZE(val) YQ_STRINGIZE_IMPL(val)
#define YQ_CONCATENATE_IMPL(x, y) x##y
#define YQ_CONCATENATE(x, y) YQ_CONCATENATE_IMPL(x, y)
// If 0 args, it mis-reports a count of 1. Also it only counts up to 10 args.
#define YQ_ARG_COUNT_IMPL(X,A,B,C,D,E,F,G,H,I,J,N,...) N
#define YQ_ARG_COUNT(...) (YQ_EXPAND(YQ_ARG_COUNT_IMPL(_,__VA_ARGS__, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)))
#ifdef __COUNTER__
# define YQ_ANONYMOUS_VARIABLE(str) YQ_CONCATENATE(str, __COUNTER__)
#else
# define YQ_ANONYMOUS_VARIABLE(str) YQ_CONCATENATE(str, __LINE__)
#endif
#define YQ_UNUSED(x) std::ignore = (x)
#ifdef __MINGW32__
// Avoid callbacks
# define YQ_CALLBACK __cdecl
# define YQ_FUNCTION_NAME __PRETTY_FUNCTION__ // __func__
#elif defined(_WIN32)
# define YQ_CALLBACK __cdecl
# define YQ_FUNCTION_NAME __FUNCTION__
#else
# define YQ_CALLBACK
# define YQ_FUNCTION_NAME __PRETTY_FUNCTION__ // __func__
#endif
// Helper marcos for doing a variable number of null checks
#define YQ_NULL_CHECK_IMPL_1(arg) if (arg == nullptr) YQ_API_LEAVE(R_InvalidParameter);
#define YQ_NULL_CHECK_IMPL_2(arg,...) YQ_NULL_CHECK_IMPL_1(arg) YQ_NULL_CHECK_IMPL_1(__VA_ARGS__)
#define YQ_NULL_CHECK_IMPL_3(arg,...) YQ_NULL_CHECK_IMPL_1(arg) YQ_NULL_CHECK_IMPL_2(__VA_ARGS__)
#define YQ_NULL_CHECK_IMPL_4(arg,...) YQ_NULL_CHECK_IMPL_1(arg) YQ_NULL_CHECK_IMPL_3(__VA_ARGS__)
#define YQ_NULL_CHECK_IMPL_5(arg,...) YQ_NULL_CHECK_IMPL_1(arg) YQ_NULL_CHECK_IMPL_4(__VA_ARGS__)
#define YQ_NULL_CHECK_IMPL_N3(N, ...) YQ_NULL_CHECK_IMPL_ ## N(__VA_ARGS__)
#define YQ_NULL_CHECK_IMPL_N2(N, ...) YQ_NULL_CHECK_IMPL_N3(N, __VA_ARGS__)
#define YQ_NULL_CHECK_IMPL_N(...) YQ_NULL_CHECK_IMPL_N2(YQ_ARG_COUNT(__VA_ARGS__), __VA_ARGS__)
// File name, line numbes, and function name are statically concatenated at compile time, with a reduced string sharing trade off
#define YQ_LOCATION __FILE__ "(" YQ_STRINGIZE(__LINE__) "): "
// Macros to log messages for debug builds.
// For release builds these will not output anything and no code or data will be generate (except for error logging)
#define YQ_LOG_WARNING(a_fmt, ...) ((BT_IsRelease) ? ((void)0) : yqLogMessage(LL_Warning, YQ_LOCATION, YQ_FUNCTION_NAME, "warning: " a_fmt, ##__VA_ARGS__))
#define YQ_LOG_DEBUG(a_fmt, ...) ((BT_IsRelease) ? ((void)0) : yqLogMessage(LL_Debug, YQ_LOCATION, YQ_FUNCTION_NAME, "debug: " a_fmt, ##__VA_ARGS__))
#define YQ_LOG_INFO(a_fmt, ...) ((BT_IsRelease) ? ((void)0) : yqLogMessage(LL_Info, YQ_LOCATION, YQ_FUNCTION_NAME, "info: " a_fmt, ##__VA_ARGS__))
#define YQ_LOG_ERROR(a_fmt, ...) yqLogMessage(LL_Error, YQ_LOCATION, YQ_FUNCTION_NAME, "error: " a_fmt, ##__VA_ARGS__)
// Macros that only generate log code in verbose debug builds. The logging logs the entry to API calls and the exit from them and what return code is being returned.
// In debug and release, on errors, the code always calls the installed or default error handling routine
enum { YQ_VERBOSE_DEBUGGING = 0 };
#define YQ_API_VERBOSE(msg) ((YQ_VERBOSE_DEBUGGING) ? YQ_LOG_DEBUG(msg) : ((void)0))
#define YQ_API_ERROR(result, ...) (yqHandleError(result, YQ_LOCATION, YQ_FUNCTION_NAME, __VA_ARGS__), YQ_LOG_ERROR("Leave: " #result " (%i)", result), result)
#define YQ_API_ENTER() YQ_API_VERBOSE("Enter")
#define YQ_API_LEAVE(result, ...) return ((result == R_Okay) ? (YQ_API_VERBOSE("Leave"), result) : YQ_API_ERROR(result, "" __VA_ARGS__))
// Macros to do common parameter and precondition checking for API function calls
// This macro checks the pre-condition is met, if it doesn't then returns the error result and executes the provided cleanup code
// See also 'finally'. Probably during pre-condition checking, there shouldn't be any cleanup yet as no work should be done at this stage.
//#define YQ_API_PRECONDITION_CLEANUP(condition,result,cleanup) do { if (!(condition)) { cleanup; YQ_API_LEAVE(result); } } while (0,0)
#define YQ_API_PRECONDITION(condition,result) do { if (!(condition)) { YQ_API_LEAVE(result, #condition); } } while (0,0)//YQ_API_PRECONDITION_CLEANUP(condition,result, ((void)0))
#define YQ_API_PRECONDITION_NULL(...) YQ_NULL_CHECK_IMPL_N(__VA_ARGS__)
// Can redirect to the user heap manager routines if provided
#define YQ_ALLOC(type) ((type*)(yqAllocateMemory(sizeof(type))))
#define YQ_ALLOC_ARRAY(type,siz) ((type*)(yqAllocateMemory(sizeof(type)*(siz))))
#define YQ_REALLOC_ARRAY(ptr,type,siz) ((type*)(yqReallocateMemory(ptr,sizeof(type)*(siz))))
#define YQ_FREE(ptr) (((ptr)) ? (void)(yqFreeMemory((void*)(ptr)), (ptr) = nullptr) : (void)0)
// C++ wrappers for using the custom allocators to replace use of new / delete (does placement new with custom allocated memory)
// Note: The custom allocators are expected to return null if the allocation fails and not throw. The macros below do not check this.
#define YQ_NEW(type) (new(YQ_ALLOC(type)) type)
#define YQ_NEW_PARAMS(type, params) (new(YQ_ALLOC(type)) type params)
#define YQ_DELETE(obj,type) ((obj) ? (void)((obj)->~type(), YQ_FREE(obj)) : (void)0)
/*
Example usage:
Result SomeFunction(T1 **Param1, T2 *Param2, T3 *Param3, int Param4)
{
YQ_API_ENTER();
// Check pre-conditions
YQ_API_PRECONDITION_NULL(Param1, Param2, Param3);
YQ_API_PRECONDITION(Param4 == 42, R_InvalidParameter);
if (errorHappened)
YQ_API_LEAVE(R_Failure);
YQ_API_LEAVE(R_Okay);
}
*/
#endif // MACROS_H