Last active
May 3, 2022 05:07
-
-
Save rileylev/0b9e5881c5b5c45bfb684bb33dacf71f to your computer and use it in GitHub Desktop.
Shadow, poison, and frezee
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "hedley.h" | |
// https://www.fluentcpp.com/2019/08/30/how-to-disable-a-warning-in-cpp/ | |
#include <initializer_list> | |
//#pragma GCC diagnostic error "-Wshadow" | |
#define PRAGMA_IGNORE_SHADOW_GCC \ | |
_Pragma("GCC diagnostic ignored \"-Wshadow\"") | |
#define MSVC_DISABLE_WARNING(num) __pragma(warning(disable : num)) | |
#define PRAGMA_IGNORE_SHADOW_MSVC \ | |
/* declaration of 'identifier' hides previous local declaration */ \ | |
MSVC_DISABLE_WARNING(4456) \ | |
/* declaration of 'identifier' hides function parameter*/ \ | |
MSVC_DISABLE_WARNING(4457) \ | |
/* declaration of 'identifier' hides class member */ \ | |
MSVC_DISABLE_WARNING(4458) \ | |
/* declaration of 'identifier' hides global declaration */ \ | |
MSVC_DISABLE_WARNING(4459) | |
#if __GNUC__ | |
# define PRAGMA_IGNORE_SHADOW PRAGMA_IGNORE_SHADOW_GCC | |
#elif _MSC_VER | |
# define PRAGMA_IGNORE_SHADOW PRAGMA_IGNORE_SHADOW_MSVC | |
#else | |
# define PRAGMA_IGNORE_SHADOW | |
#endif | |
#define IGNORE_SHADOW(...) \ | |
HEDLEY_DIAGNOSTIC_PUSH PRAGMA_IGNORE_SHADOW __VA_ARGS__ \ | |
HEDLEY_DIAGNOSTIC_POP | |
#define GENSYM(sym) HEDLEY_CONCAT3(sym, gensym, __COUNTER__) | |
/** | |
* Defines a new variable in the introduced scope | |
* | |
* LET1(int x = 3){ | |
* ++x; | |
* } | |
*/ | |
#define LET1(...) for(__VA_ARGS__; [[maybe_unused]] auto GENSYM(_) : {0}) | |
/** | |
* Disable warnings for shadowing for one variable definition introduced in | |
* a new scope | |
* | |
* int const x = 2; | |
* SHADOW(int x = 0){ | |
* ++x; // ok! | |
* } | |
*/ | |
#define SHADOW(...) IGNORE_SHADOW(LET1(__VA_ARGS__)) | |
/** | |
* Poisons a variable within the introduced scope | |
* | |
* int x = 0; | |
* POISON(x){ | |
* int y = x; // warning/error! | |
* } | |
*/ | |
#define POISON(name) \ | |
SHADOW([[deprecated("poisoned"), maybe_unused]] char name) | |
#define FREEZE_(tmp, name) \ | |
LET1(auto const& tmp = name) SHADOW(auto const& name = tmp) | |
/** | |
* Freezes a variable into a const within the new scope: | |
* | |
* for(int i =0; i < 10; ++i) | |
* FREEZE(i){ | |
* ++i; // error! i is const here | |
* } | |
*/ | |
#define FREEZE(name) FREEZE_(GENSYM(tmp), name) | |
#include <type_traits> | |
template<class T> | |
using ReadIn = std::conditional_t< | |
std::is_trivially_copyable_v<T> && sizeof(T) <= 2 * sizeof(void*), | |
T const, | |
T const&>; | |
#define READIN_(tmp, name) \ | |
LET1(auto const& tmp = name) \ | |
SHADOW(ReadIn<std::remove_reference_t<decltype(tmp)>> name = tmp) | |
/** | |
* Rebind `name` to a ReadIn within the new scope | |
* | |
* To pass by value or const&? How expensive is a copy? How much do | |
* references befuddle the optimizer? Can we automate this decision without | |
* interfering with template type deduction? | |
* | |
* Using READIN, we can take a variable by * const& and rebind it to a | |
* ReadIn, copying if that's cheap. | |
* | |
* template<class T> | |
* auto f(T const& x) { | |
* READIN(x){ | |
* // now x becomes a copy if that's cheap | |
* // without messing with template type deduction | |
* } | |
* } | |
* | |
* The template function is inlinable, so the optimizer can (with luck) see | |
* if we end up taking a copy. | |
*/ | |
#define READIN(name) READIN_(GENSYM(tmp), name) | |
int const x = 0; | |
int test() { | |
SHADOW(int x = 2) { | |
++x; | |
auto y = x; | |
POISON(x) { | |
++x; | |
FREEZE(y) { return ++y; } | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
https://godbolt.org/z/Yq4xafnYM