├── .travis.yml ├── LICENSE ├── README.md ├── live.cpp ├── live.hpp └── sample.cc /.travis.yml: -------------------------------------------------------------------------------- 1 | language: cpp 2 | 3 | compiler: 4 | - clang 5 | - gcc 6 | 7 | install: 8 | - wget --quiet -O - https://raw.githubusercontent.com/r-lyeh/depot/master/travis.pre.sh | bash -x 9 | 10 | script: 11 | - wget --quiet -O - https://raw.githubusercontent.com/r-lyeh/depot/master/travis.build.sh | bash -x 12 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015 r-lyeh (https://github.com/r-lyeh) 2 | 3 | This software is provided 'as-is', without any express or implied 4 | warranty. In no event will the authors be held liable for any damages 5 | arising from the use of this software. 6 | 7 | Permission is granted to anyone to use this software for any purpose, 8 | including commercial applications, and to alter it and redistribute it 9 | freely, subject to the following restrictions: 10 | 11 | 1. The origin of this software must not be misrepresented; you must not 12 | claim that you wrote the original software. If you use this software 13 | in a product, an acknowledgment in the product documentation would be 14 | appreciated but is not required. 15 | 2. Altered source versions must be plainly marked as such, and must not be 16 | misrepresented as being the original software. 17 | 3. This notice may not be removed or altered from any source distribution. 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | live 2 | ==== 3 | 4 | - Live is an automatic reloader of constants during runtime, featuring type inference. Written in C++11. 5 | - Live is cross-platform. Builds on Windows/Linux/MacosX. Compiles on g++/clang/msvc. 6 | - Live is self-contained. No third party dependencies. 7 | - Live is tiny. Header only. 8 | - Live is zlib/libpng licensed. 9 | 10 | ### intro 11 | - On debug, `$live(...)` constants are watched, parsed, evaluated, cached, and returned back as needed. 12 | - On release, `$live(...)` constants are just returned back with no modifications. 13 | 14 | ### sample 15 | ```c++ 16 | #include 17 | #include 18 | #include "live.hpp" 19 | 20 | int main() { 21 | // feel free to modify following constants during runtime 22 | for(;;) { 23 | int number = $live(-1234); 24 | double real = $live(3.14159); 25 | const char *string = $live("hello world"); 26 | std::string string2 = $live("abcdef"); 27 | std::cout << number << ',' << real << ',' << string << ',' << string2 << std::endl; 28 | } 29 | } 30 | ``` 31 | 32 | ### cons 33 | - Live requires strict ordering of `$live()` elements during runtime. 34 | -------------------------------------------------------------------------------- /live.cpp: -------------------------------------------------------------------------------- 1 | #include "live.hpp" 2 | -------------------------------------------------------------------------------- /live.hpp: -------------------------------------------------------------------------------- 1 | // Simple live reloading of variables, featuring type inference. Written in C++11 2 | // - rlyeh, zlib/libpng license (2014) 3 | 4 | #pragma once 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include 16 | #include 17 | 18 | #if defined(_WIN32) 19 | # include 20 | #else 21 | # include 22 | # include 23 | #endif 24 | 25 | #ifndef LIVE_TOKEN_IDENTIFIER 26 | #define LIVE_TOKEN_IDENTIFIER "$live" 27 | #endif 28 | 29 | namespace { 30 | 31 | class livemonitor { 32 | 33 | static bool timestamp( const char * const pathfile, bool modify ) { 34 | auto date = [&]() -> time_t { 35 | struct stat fileInfo; 36 | if( stat( pathfile, &fileInfo ) < 0 ) 37 | return std::time(0); 38 | return fileInfo.st_mtime; 39 | }; 40 | 41 | time_t curtime = date(); 42 | 43 | static std::map< const char * const, time_t > cache; 44 | if( cache.find( pathfile ) == cache.end() ) { 45 | cache[ pathfile ] = curtime; 46 | return true; 47 | } 48 | 49 | auto &modtime = cache[ pathfile ]; 50 | if( std::difftime( modtime, curtime ) != 0 ) { 51 | if( modify ) modtime = curtime; 52 | return true; 53 | } 54 | 55 | return false; 56 | }; 57 | 58 | static bool has_changed( const char * const pathfile ) { 59 | return timestamp( pathfile, false ); 60 | } 61 | 62 | static void touch( const char * const pathfile ) { 63 | timestamp( pathfile, true ); 64 | } 65 | 66 | static std::vector< std::string > &get( const char *const pathfile ) { 67 | static std::map< const char *const, std::vector< std::string > > cache; 68 | return cache[ pathfile ]; 69 | } 70 | 71 | static void parse( const char *const pathfile ) { 72 | auto split = []( const std::string &str, const std::string &delimiters ) { 73 | std::string map( 256, '\0' ); 74 | for( const unsigned char &ch : delimiters ) 75 | map[ ch ] = '\1'; 76 | std::vector< std::string > tokens(1); 77 | for( const unsigned char &ch : str ) { 78 | /**/ if( !map.at(ch) ) tokens.back().push_back( ch ); 79 | else if( tokens.back().size() ) tokens.push_back( std::string() ); 80 | } 81 | while( tokens.size() && !tokens.back().size() ) tokens.pop_back(); 82 | return tokens; 83 | }; 84 | auto trim = [&]( std::string &str, const char *chars ) { 85 | // left 86 | auto begin = str.find_first_not_of(chars); 87 | if( std::string::npos != begin ) { 88 | str = str.substr( begin ); 89 | } 90 | // right 91 | auto end = str.find_last_not_of(chars); 92 | if( std::string::npos != end ) { 93 | str = str.substr( 0, end+1 ); 94 | } 95 | }; 96 | 97 | auto &cache = get( pathfile ); 98 | cache.clear(); 99 | 100 | std::stringstream data; 101 | std::ifstream ifs( pathfile ); 102 | data << ifs.rdbuf(); 103 | 104 | auto words = split( data.str(), "()\r\n" ); 105 | for( auto it = words.begin(), end = words.end(); it != end; ++it ) { 106 | auto tokens = split( *it, "<>!=;+- "); 107 | for( auto &token : tokens ) { 108 | if( LIVE_TOKEN_IDENTIFIER == token ) { 109 | cache.push_back( *(++it) ); 110 | trim( cache.back(), " \t" ); 111 | trim( cache.back(), "\"" ); 112 | } 113 | } 114 | } 115 | } 116 | 117 | template< typename T > 118 | static void cast( const std::string &value, T &t ) { 119 | T t_; 120 | std::stringstream ss; 121 | ss << std::setprecision( std::numeric_limits::digits10 + 2 ); 122 | ss << value; 123 | t = ss >> t_ ? t_ : t; 124 | } 125 | static void cast( const std::string &value, std::string &t ) { 126 | std::stringstream ss( value ); 127 | std::getline( ss, t ); 128 | } 129 | 130 | public: 131 | 132 | template< typename T > 133 | static T &check( const char *const pathfile, int counter, const T &t ) { 134 | using pair = std::pair< const char *const, int >; 135 | static std::map< pair, T > cache; 136 | static std::map< pair, unsigned > hits; 137 | auto key = pair( pathfile, counter ); 138 | T &item = cache[ key ]; 139 | if( has_changed( pathfile ) ) { 140 | parse( pathfile ); 141 | auto &values = get( pathfile ); 142 | if( counter < values.size() ) { 143 | auto &value = values.at( counter ); 144 | if( !value.empty() ) { 145 | cast( value, item ); 146 | } 147 | } 148 | if( ++hits[ key ] >= values.size() ) { 149 | hits[ key ] = 0; 150 | touch( pathfile ); 151 | } 152 | } 153 | return item; 154 | } 155 | 156 | template< typename T, size_t N > 157 | static const char *check( const char *const pathfile, int counter, const char (&t)[N] ) { 158 | return check( pathfile, counter, std::string(t) ).c_str(); 159 | } 160 | }; 161 | } 162 | 163 | #undef $live 164 | 165 | #if defined(NDEBUG) || defined(_NDEBUG) || defined(RELEASE) || defined(MASTER) || defined(GOLD) 166 | #define $live(...) (__VA_ARGS__) 167 | #else 168 | #define $live(...) livemonitor::check( __FILE__,__COUNTER__,__VA_ARGS__ ) 169 | #endif 170 | -------------------------------------------------------------------------------- /sample.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "live.hpp" 4 | 5 | int main() { 6 | // feel free to modify following constants during runtime 7 | for(;;) { 8 | int number = $live(-1234); 9 | double real = $live(3.14159); 10 | const char *string = $live("hello world"); 11 | std::string string2 = $live("abcdef"); 12 | std::cout << number << ',' << real << ',' << string << ',' << string2 << std::endl; 13 | } 14 | } 15 | --------------------------------------------------------------------------------