Here is how I would do it:
1.) Use the str_const template for the str_const string operation described here: Convenient declaration of compile-time strings in C ++
the code:
class str_const { // constexpr string private: const char * const p_; const std::size_t sz_; public: template <std::size_t N> constexpr str_const(const char(&a)[N]) : p_(a) , sz_(N - 1) {} constexpr char operator[](std::size_t n) const { return n < sz_ ? p_[n] : throw std::out_of_range(""); } constexpr std::size_t size() const { return sz_; } constexpr const char * get() const { return p_; } };
This allows you to do something like str_const message = "Invalid license" and manipulate message in constexpr functions.
2.) Make a simple pseudo-random compilation generator using the __TIME__ and __LINE__ to generate the seed. This is described in detail here: Generate random numbers in C ++ at compile time
They give code based on patterns.
3.) Create a struct with constexpr ctor that accepts either const char [] or the patterns themselves against the size, similar to str_const , or just takes str_const , and generates two str_const which is its member variables.
- A
str_const length n containing unsigned pseudo-random characters generated using a pseudo-random generator, where n is the input length. ("noise string") - A
str_const length n containing the input mutation (as unsigned characters) of input characters with noise characters. ("ciphertext")
Then it has a decrypt member function, which does not have to be constexpr and can return std::string , which simply subtracts each character of the noise line from the corresponding character of the ciphertext and returns the resulting string.
If your compiler still stores the source string literal in binary format, it means that it stores the input string literal (constructor argument), which I donβt think it should do from the moment it is temporary or basically by inserting the decrypt function, and you should be able to prevent this by obfuscating it with function pointers or labeling it volatile or similar.
Edit: I'm not sure if the standard requires that constexpr temporary objects not display in binary format. Actually, I'm curious now. I expect that at least in the release build, a good compiler should remove them when they are no longer needed.
Edit: So you have already accepted my answer. But in any case, for completeness, there is some source code that implements the above ideas using only the C ++ 11 standard. It works on gcc-4.9 and clang-3.6, even when the optimization is disabled, as far as I can tell.
#include <array> #include <iostream> #include <string> typedef uint32_t u32; typedef uint64_t u64; typedef unsigned char uchar; template<u32 S, u32 A = 16807UL, u32 C = 0UL, u32 M = (1UL<<31)-1> struct LinearGenerator { static const u32 state = ((u64)S * A + C) % M; static const u32 value = state; typedef LinearGenerator<state> next; struct Split { // Leapfrog typedef LinearGenerator< state, A*A, 0, M> Gen1; typedef LinearGenerator<next::state, A*A, 0, M> Gen2; }; }; // Metafunction to get a particular index from generator template<u32 S, std::size_t index> struct Generate { static const uchar value = Generate<LinearGenerator<S>::state, index - 1>::value; }; template<u32 S> struct Generate<S, 0> { static const uchar value = static_cast<uchar> (LinearGenerator<S>::value); }; // List of indices template<std::size_t...> struct StList {}; // Concatenate template<typename TL, typename TR> struct Concat; template<std::size_t... SL, std::size_t... SR> struct Concat<StList<SL...>, StList<SR...>> { typedef StList<SL..., SR...> type; }; template<typename TL, typename TR> using Concat_t = typename Concat<TL, TR>::type; // Count from zero to n-1 template<size_t s> struct Count { typedef Concat_t<typename Count<s-1>::type, StList<s-1>> type; }; template<> struct Count<0> { typedef StList<> type; }; template<size_t s> using Count_t = typename Count<s>::type; // Get a scrambled character of a string template<u32 seed, std::size_t index, std::size_t N> constexpr uchar get_scrambled_char(const char(&a)[N]) { return static_cast<uchar>(a[index]) + Generate<seed, index>::value; } // Get a ciphertext from a plaintext string template<u32 seed, typename T> struct cipher_helper; template<u32 seed, std::size_t... SL> struct cipher_helper<seed, StList<SL...>> { static constexpr std::array<uchar, sizeof...(SL)> get_array(const char (&a)[sizeof...(SL)]) { return {{ get_scrambled_char<seed, SL>(a)... }}; } }; template<u32 seed, std::size_t N> constexpr std::array<uchar, N> get_cipher_text (const char (&a)[N]) { return cipher_helper<seed, Count_t<N>>::get_array(a); } // Get a noise sequence from a seed and string length template<u32 seed, typename T> struct noise_helper; template<u32 seed, std::size_t... SL> struct noise_helper<seed, StList<SL...>> { static constexpr std::array<uchar, sizeof...(SL)> get_array() { return {{ Generate<seed, SL>::value ... }}; } }; template<u32 seed, std::size_t N> constexpr std::array<uchar, N> get_key() { return noise_helper<seed, Count_t<N>>::get_array(); } /* // Get an unscrambled character of a string template<u32 seed, std::size_t index, std::size_t N> char get_unscrambled_char(const std::array<uchar, N> & a) { return static_cast<char> (a[index] - Generate<seed, index>::value); } */ // Metafunction to get the size of an array template<typename T> struct array_info; template <typename T, size_t N> struct array_info<T[N]> { typedef T type; enum { size = N }; }; template <typename T, size_t N> struct array_info<const T(&)[N]> : array_info<T[N]> {}; // Scramble a string template<u32 seed, std::size_t N> class obfuscated_string { private: std::array<uchar, N> cipher_text_; std::array<uchar, N> key_; public: explicit constexpr obfuscated_string(const char(&a)[N]) : cipher_text_(get_cipher_text<seed, N>(a)) , key_(get_key<seed,N>()) {} operator std::string() const { char plain_text[N]; for (volatile std::size_t i = 0; i < N; ++i) { volatile char temp = static_cast<char>( cipher_text_[i] - key_[i] ); plain_text[i] = temp; } return std::string{plain_text, plain_text + N}; } }; template<u32 seed, std::size_t N> std::ostream & operator<< (std::ostream & s, const obfuscated_string<seed, N> & str) { s << static_cast<std::string>(str); return s; } #define RNG_SEED ((__TIME__[7] - '0') * 1 + (__TIME__[6] - '0') * 10 + \ (__TIME__[4] - '0') * 60 + (__TIME__[3] - '0') * 600 + \ (__TIME__[1] - '0') * 3600 + (__TIME__[0] - '0') * 36000) + \ (__LINE__ * 100000) #define LIT(STR) \ obfuscated_string<RNG_SEED, array_info<decltype(STR)>::size>{STR} auto S2 = LIT(("Hewwo, I'm hunting wabbits")); int main() { constexpr auto S1 = LIT(("What up doc")); std::cout << S1 << std::endl; std::cout << S2 << std::endl; }