// ================================================================ //
//                                                                  //
//   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
#ifndef STRINGIZE_H
#include "stringize.h"
#endif

#if defined(__cplusplus)
# if (__cplusplus < 201402L)
#  error requires at least C++14 standard
// Note: hard to raise that minimum standard w/o kicking RNACMA. see ../RNACMA/Makefile@binder1st
# elif (__cplusplus == 201402L)
// minimum language standard: C++14
# elif (__cplusplus == 201703L)
#  define ARB_ENABLE17_FEATURES
# elif (__cplusplus == 202002L)
#  define ARB_ENABLE20_FEATURES
# else
#  pragma message "__cplusplus is " stringize_pscan(__cplusplus)
#  error "Unsupported C++ standard defined in __cplusplus"
# endif
#else
# error plain C compilation includes cxxforward.h
#endif

#ifndef _GLIBCXX_CSTDDEF
#include <cstddef>
#endif

// ---------------
//      Cxx20

#ifdef ARB_ENABLE20_FEATURES
# define ARB_ENABLE17_FEATURES
// C++20 is enabled starting with gcc 11.1 in ../Makefile@USE_Cxx20
// no Cxx20 specifica used yet
// check what features could be forward defined here - see https://en.wikipedia.org/wiki/C%2B%2B20
#endif

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

#ifdef ARB_ENABLE17_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

// --------------------------------------------------
// Legacy defines for pre-C++14 compatibility.
// Now obsolete since C++14 is always supported.
// Safe to inline and remove.

// 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

// 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_ENABLE17_FEATURES
#undef ARB_ENABLE20_FEATURES

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