// ================================================================ //
//                                                                  //
//   File      : cxxforward.h                                       //
//   Purpose   : macros for forward usage of C++11 features         //
//               w/o loosing compatibility to C++03 compilers       //
//                                                                  //
//   Coded by Ralf Westram (coder@reallysoft.de) in December 2012   //
//   Institute of Microbiology (Technical University Munich)        //
//   http://www.arb-home.de/                                        //
//                                                                  //
// ================================================================ //

#ifndef CXXFORWARD_H
#define CXXFORWARD_H

#ifndef GCCVER_H
#include "gccver.h"
#endif
#if defined(__cplusplus)
# if (GCC_VERSION_CODE >= 407) || defined(__clang__)
#  if (__cplusplus == 199711L)
#  else
#   if (__cplusplus == 201103L)
#    define ARB_ENABLE11_FEATURES
#   else
#    if (__cplusplus == 201402L)
#     define ARB_ENABLE14_FEATURES
#    else
#     if (__cplusplus == 201703L)
#      define ARB_ENABLE17_FEATURES
#     else
#      error Unknown C++ standard defined in __cplusplus
#     endif
#    endif
#   endif
#  endif
# endif
#else
# error C compilation includes cxxforward.h
#endif

#ifndef _GLIBCXX_CSTDDEF
#include <cstddef>
#endif

// ---------------
//      Cxx17

#ifdef ARB_ENABLE17_FEATURES
# define ARB_ENABLE14_FEATURES
// C++17 is enabled starting with gcc 8.1 in ../Makefile@USE_Cxx17
// no Cxx17 specifica used yet
// @@@ check what features could be forward defined here - see https://en.wikipedia.org/wiki/C%2B%2B17
#endif

// ---------------
//      Cxx14

#ifdef ARB_ENABLE14_FEATURES
# define ARB_ENABLE11_FEATURES
// C++14 is enabled starting with gcc 6.1 in ../Makefile@USE_Cxx14
//
// Use #ifdef Cxx14 to insert conditional sections using full C++14
# define Cxx14 1

// C++14 allows more complex constexpr functions (e.g. multiple commands; void return type)
# define CONSTEXPR_INLINE_Cxx14 constexpr inline

#else
// backward (non C++14) compatibility defines:

# define CONSTEXPR_INLINE_Cxx14 inline

#endif

// ---------------
//      Cxx11

#ifdef ARB_ENABLE11_FEATURES
// C++11 is enabled starting with gcc 4.7 in ../Makefile@USE_Cxx11
//
// Full support for C++11 is available starting with gcc 4.8.
// Use #ifdef Cxx11 to insert conditional sections using full C++11
# if (GCC_VERSION_CODE >= 408)
#  define Cxx11 1
# endif

// allows static member initialisation in class definition:
# define CONSTEXPR        constexpr
# define CONSTEXPR_INLINE constexpr inline

// allows to protect overloading functions against signature changes of overload functions:
# define OVERRIDE override

// allows additional optimization of virtual calls
// (does not only allow to replace virtual by a normal call, it also allows inlining!)
# define FINAL_TYPE     final
# define FINAL_OVERRIDE final override

# define NULp nullptr

#else
// backward (non C++11) compatibility defines:

# if (GCC_VERSION_CODE >= 406)
#  define CONSTEXPR        constexpr
# else
#  define CONSTEXPR        const
# endif

# define CONSTEXPR_INLINE  inline
# define OVERRIDE
# define FINAL_TYPE
# define FINAL_OVERRIDE

# define NULp NULL
// [Note: defining NULp as 0 causes failing tests if compiled with gcc 4.4.3; com-errors somewhere in AISC interface]

#endif

// Note: additional (experimental) constexpr macros are defined in ../CORE/arb_assert.h@ASSERTING_CONSTEXPR_INLINE

// workaround-defines for situations where clang behaves differently than gcc (NC = Not for Clang)
#if defined(__clang__)
# define CONSTEXPR_INLINE_NC inline
#else
# define CONSTEXPR_INLINE_NC CONSTEXPR_INLINE
#endif


// make use of gcc 7.x implicit fallthrough warnings:
#if (GCC_VERSION_CODE >= 700)
# define FALLTHROUGH [[gnu::fallthrough]]
// Note: Cxx17 may use [[fallthrough]]
#else
# define FALLTHROUGH
// Note: clang may know [[clang::fallthrough]]
#endif

// hide warnings about intentionally unused things:
#if (GCC_VERSION_CODE >= 700)
# define UNUSED [[gnu::unused]]
// Note: Cxx17 may use [[unused]]
#else
# define UNUSED __attribute__((unused))
#endif


// allow to hide unwanted final suggestions
#ifdef SUGGESTS_FINAL

# if (GCC_VERSION_CODE >= 900)
// test with older gcc versions?! SUGGESTS_FINAL defined for gcc 5.0++
#  define EXTENDED_FINAL_WARNING_SUPPRESSION // gcc 9.1 became too smart (for old suppression methods)
# endif

namespace final_unsuggest { struct fakedarg { }; };
# define NF_JOIN(X,Y) X##Y

# ifdef EXTENDED_FINAL_WARNING_SUPPRESSION

// declare fake-ctor used by MARK_NONFINAL_CLASS (has to be added into BASE)
#  define PREPARE_MARK_NONFINAL_CLASS(CLASS) explicit CLASS(final_unsuggest::fakedarg)

#  define MARK_NONFINAL_CLASS(BASE)                                             \
    namespace final_unsuggest {                                                 \
        struct UNUSED NF_JOIN(unfinalize,BASE) final : BASE {                   \
            NF_JOIN(unfinalize,BASE)() : BASE(final_unsuggest::fakedarg()) {}   \
        };                                                                      \
    }

// PARAMS has to contain parentheses and attributes like const!
// RETURN has to contain 'return' keyword or may be completely empty
#  define MARK_NONFINAL__INTERNAL(BASE,RETYPE,METHOD_NAME,PARAMS,RETURN)        \
    namespace final_unsuggest {                                                 \
        struct UNUSED NF_JOIN(BASE,METHOD_NAME) final : BASE {                  \
            RETYPE METHOD_NAME PARAMS override {                                \
                RETURN;                                                         \
            }                                                                   \
        };                                                                      \
    }

// some final type suggestion even do not get silenced by the above code.
// => disable -Wsuggest-final-types locally using
// GCC_TOO_SMART_FOR_USEFUL_FINAL_TYPE_SUGGESTION

#  if (GCC_VERSION_CODE >= 901) && (GCC_VERSION_CODE <= 905) // (please do not activate for all future versions, test each of them)
#   define GCC_TOO_SMART_FOR_USEFUL_FINAL_TYPE_SUGGESTION
# endif
#  if (GCC_VERSION_CODE >= 1001) && (GCC_VERSION_CODE <= 1005) // (please do not activate for all future versions, test each of them)
#   define GCC_TOO_SMART_FOR_USEFUL_FINAL_TYPE_SUGGESTION
# endif

# else // !EXTENDED_FINAL_WARNING_SUPPRESSION

#  define PREPARE_MARK_NONFINAL_CLASS(CLASS)
#  define MARK_NONFINAL_CLASS(BASE)                             \
    namespace final_unsuggest {                                 \
        struct UNUSED NF_JOIN(unfinalize,BASE) final : BASE {   \
        };                                                      \
    }
// PARAMS has to contain parentheses and attributes like const!
// RETURN is ignored here (needed in version above)
#  define MARK_NONFINAL__INTERNAL(BASE,RETYPE,METHOD_NAME,PARAMS,RETURN)        \
    namespace final_unsuggest {                                                 \
        struct UNUSED NF_JOIN(BASE,METHOD_NAME) final : BASE {                  \
            inline RETYPE METHOD_NAME PARAMS override;                          \
        };                                                                      \
    }

# endif

#else

# define PREPARE_MARK_NONFINAL_CLASS(CLASS)
# define MARK_NONFINAL_CLASS(BASE)
# define MARK_NONFINAL__INTERNAL(BASE,RETURN_TYPE,METHOD_NAME,PARAMS,RETURN)

#endif


#define MARK_NONFINAL_DTOR(BASE) MARK_NONFINAL_CLASS(BASE)

// like MARK_NONFINAL_FUNCTION, but void "result":
# define MARK_NONFINAL_FUNCTION(BASE,RETYPE,METHOD_NAME,PARAMS,RETVAL) MARK_NONFINAL__INTERNAL(BASE,RETYPE,METHOD_NAME,PARAMS,return RETVAL)
# define MARK_NONFINAL_METHOD(BASE,METHOD_NAME,PARAMS)                 MARK_NONFINAL__INTERNAL(BASE,void,METHOD_NAME,PARAMS,)


#undef ARB_ENABLE11_FEATURES
#undef ARB_ENABLE14_FEATURES

#else
#error cxxforward.h included twice
#endif // CXXFORWARD_H
