├── .travis.yml ├── LICENSE ├── README.md ├── frodo.cc └── frodo.hpp /.travis.yml: -------------------------------------------------------------------------------- 1 | language: cpp 2 | sudo: required 3 | 4 | compiler: 5 | - clang 6 | - gcc 7 | 8 | install: 9 | - wget --quiet -O - https://raw.githubusercontent.com/r-lyeh/depot/master/travis.pre.sh | bash -x 10 | 11 | script: 12 | - wget --quiet -O - https://raw.githubusercontent.com/r-lyeh/depot/master/travis.build.sh | bash -x 13 | # - wget --quiet -O - https://raw.githubusercontent.com/r-lyeh/depot/master/travis.run.sh | bash -x 14 | -------------------------------------------------------------------------------- /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 | frodo 2 | ===== 3 | 4 | - Frodo is a lightweight ring dependency framework (C++11). 5 | - Frodo is tiny, header-only, cross-platform. 6 | - Frodo is zlib/libpng licensed. 7 | 8 | ## Some theory 9 | - Rings are made of independant systems, subsystems, libraries, singletons, etc. 10 | - Inner rings provide functionality to outer rings. 11 | - Initialization and deinitialization of rings must follow strict order. 12 | 13 | ## API 14 | ```c++ 15 | namespace frodo { 16 | struct level { 17 | std::string name; 18 | std::function init; 19 | std::function quit; 20 | }; 21 | 22 | bool ring( int lvl, const level &impl ); 23 | bool init(); 24 | bool alive(); 25 | bool reboot( int lvl_target ); 26 | bool quit(); 27 | } 28 | ``` 29 | 30 | ## Showcase 31 | ```c++ 32 | #include "frodo.hpp" 33 | #include 34 | 35 | namespace memory { 36 | bool init() { 37 | std::cout << "[ OK ] mem setup" << std::endl; 38 | return true; 39 | } 40 | bool quit() { 41 | std::cout << "[ OK ] mem teardown" << std::endl; 42 | return true; 43 | } 44 | } 45 | 46 | namespace logger { 47 | bool init() { 48 | std::cout << "[ OK ] logger setup" << std::endl; 49 | return true; 50 | } 51 | bool quit() { 52 | std::cout << "[ OK ] logger teardown" << std::endl; 53 | return true; 54 | } 55 | } 56 | 57 | namespace console { 58 | bool init() { 59 | std::cout << "[ OK ] console setup" << std::endl; 60 | return true; 61 | } 62 | bool quit() { 63 | std::cout << "[ OK ] console teardown" << std::endl; 64 | return true; 65 | } 66 | } 67 | 68 | int main() { 69 | // app-defined levels 70 | // 00 memory and hooks 71 | frodo::ring( 0, { "memory", memory::init, memory::quit } ); 72 | // 10 subsystems 73 | frodo::ring( 13, { "logger", logger::init, logger::quit } ); 74 | frodo::ring( 14, { "console", console::init, console::quit } ); 75 | // 20 devices 76 | //frodo::ring( 20, { "audio", audio::init, audio::quit } ); 77 | //frodo::ring( 25, { "data", data::init, data::quit } ); 78 | //frodo::ring( 27, { "input", input::init, input::quit } ); 79 | // 30 opengl 80 | //frodo::ring( 34, { "ui", ui::init, ui::quit } ); 81 | //frodo::ring( 30, { "opengl", gl::init, gl::quit } ); 82 | //frodo::ring( 31, { "monitor", monitor::init, monitor::quit } ); 83 | //frodo::ring( 35, { "font", font::init, font::quit } ); 84 | // 40 game 85 | //frodo::ring( 40, { "model", model::init, model::quit } ); 86 | //frodo::ring( 45, { "world", world::init, world::quit } ); 87 | // 50 ui 88 | //frodo::ring( 59, { "help", help::init, help::quit } ); 89 | 90 | if( frodo::init() ) { 91 | // app starts here 92 | std::string dummy; 93 | std::cout << "Press any key to continue... (try aborting or killing app too)" << std::endl; 94 | std::getline( std::cin, dummy ); 95 | 96 | // shutdown 97 | if( frodo::quit() ) { 98 | return 0; 99 | } 100 | } 101 | 102 | return -1; 103 | } 104 | ``` 105 | 106 | ## Possible output 107 | ```c++ 108 | $frodo ./sample.out 109 | [ OK ] mem setup 110 | [ OK ] logger setup 111 | [ OK ] console setup 112 | Press any key to continue... (try aborting or killing app too) 113 | ^C 114 | [ OK ] console teardown 115 | [ OK ] logger teardown 116 | [ OK ] mem teardown 117 | ``` 118 | 119 | ## Todo 120 | - `init({signal...})` 121 | - `reboot(lvl)` 122 | 123 | ### Changelog 124 | - v1.0.1 (2016/04/11): No STL allocations; Implement reboot 125 | - v1.0.0 (2015/12/07): Header-only, simplified implementation 126 | - v0.0.0 (2015/08/03): Initial commit 127 | -------------------------------------------------------------------------------- /frodo.cc: -------------------------------------------------------------------------------- 1 | #define FRODO_BUILD_DEMO 2 | #include "frodo.hpp" 3 | -------------------------------------------------------------------------------- /frodo.hpp: -------------------------------------------------------------------------------- 1 | // Frodo, a ring dependency framework. zlib/libpng licensed. 2 | // - rlyeh ~~ listening to stoned jesus / i'm the mountain 3 | 4 | #pragma once 5 | 6 | #define FRODO_VERSION "1.0.1" /* (2016/04/11) No STL allocations; Implement reboot 7 | #define FRODO_VERSION "1.0.0" // (2015/12/07) Header-only, simplified implementation 8 | #define FRODO_VERSION "0.0.0" // (2015/08/03) Initial commit */ 9 | 10 | #include 11 | 12 | // public api 13 | 14 | #ifndef FRODO_INSTALL_SIGHANDLERS 15 | #define FRODO_INSTALL_SIGHANDLERS 0 16 | #endif 17 | 18 | namespace frodo { 19 | struct level { 20 | const char *name; 21 | bool (*init)(); 22 | bool (*quit)(); 23 | }; 24 | 25 | bool ring( int lvl, const level &impl ); 26 | bool init( bool display = false, int from_lvl = 0, int to_lvl = 256 ); 27 | bool alive(); 28 | bool reboot( bool display = false, int lvl = 0 ); 29 | bool quit( bool display = false, int lvl = 0 ); 30 | } 31 | 32 | // api details 33 | 34 | #include 35 | #include 36 | #include 37 | 38 | namespace frodo { 39 | 40 | struct singleton { 41 | frodo::level map[256]; 42 | frodo::level unmap[256]; 43 | volatile sig_atomic_t is_exiting = 0, is_expected = 0; 44 | 45 | static void sigkill( int sig ) { 46 | /* 47 | The SIGKILL signal is used to cause immediate program termination. It cannot be handled or ignored, and is therefore always fatal. It is also not possible to block this signal. 48 | This signal is usually generated only by explicit request. Since it cannot be handled, you should generate it only as a last resort, after first trying a less drastic method such as C-c or SIGTERM. If a process does not respond to any other termination signals, sending it a SIGKILL signal will almost always cause it to go away. 49 | In fact, if SIGKILL fails to terminate a process, that by itself constitutes an operating system bug which you should report. 50 | The system will generate SIGKILL for a process itself under some unusual conditions where the program cannot possibly continue to run (even to run a signal handler). 51 | */ 52 | singleton::get().is_exiting = 1; 53 | } 54 | static void sigterm( int sig ) { 55 | /* 56 | The SIGTERM signal is a generic signal used to cause program termination. Unlike SIGKILL, this signal can be blocked, handled, and ignored. It is the normal way to politely ask a program to terminate. 57 | The shell command kill generates SIGTERM by default. 58 | */ 59 | singleton::get().is_expected = 1; 60 | singleton::get().is_exiting = 1; 61 | } 62 | static void sigint( int sig ) { 63 | /* 64 | The SIGINT (“program interrupt”) signal is sent when the user types the INTR character (normally C-c). See Special Characters, for information about terminal driver support for C-c. 65 | */ 66 | singleton::get().is_expected = 1; 67 | singleton::get().is_exiting = 1; 68 | } 69 | static void sighup( int sig ) { 70 | /* 71 | The SIGHUP (“hang-up”) signal is used to report that the user’s terminal is disconnected, perhaps because a network or telephone connection was broken. For more information about this, see Control Modes. 72 | This signal is also used to report the termination of the controlling process on a terminal to jobs associated with that session; this termination effectively disconnects all processes in the session from the controlling terminal. For more information, see Termination Internals. 73 | */ 74 | singleton::get().is_expected = 1; 75 | singleton::get().is_exiting = 1; 76 | } 77 | 78 | static void void_quit() { 79 | singleton::get().is_expected = 1; 80 | frodo::quit(); 81 | } 82 | 83 | static singleton &get() { 84 | static singleton st; 85 | # if FRODO_INSTALL_SIGHANDLERS 86 | static bool installed = (std::atexit( void_quit ), signal(SIGTERM, sigterm), signal(SIGINT, sigint), true); 87 | # endif 88 | return st; 89 | } 90 | }; 91 | 92 | inline bool ring( int lvl, const level &def ) { 93 | auto &map = singleton::get().map; 94 | map[ lvl ] = def; 95 | return true; 96 | } 97 | 98 | inline bool init( bool display, int from, int to ) { 99 | auto &map = singleton::get().map; 100 | auto &unmap = singleton::get().unmap; 101 | bool ok = true; 102 | for( auto j = from; ok && j < to; ++j ) { 103 | const auto &ring = map[j]; 104 | if( ring.init ) { 105 | if( display ) printf( "+ %s\n", ring.name); 106 | ok &= ring.init(); 107 | if( ok ) { 108 | unmap[ j ] = ring; 109 | } 110 | } 111 | } 112 | return ok; 113 | } 114 | 115 | inline bool quit( bool display, int lvl ) { 116 | auto &is_expected = singleton::get().is_expected; 117 | if( !is_expected ) { 118 | //assert( !"unexpected quit()" ); 119 | } 120 | auto &unmap = singleton::get().unmap; 121 | bool ok = true; 122 | for( auto j = 256; ok && (--j >= lvl); ) { 123 | auto &ring = unmap[ j ]; 124 | if( ring.quit ) { 125 | if( display ) printf( "- %s\n", ring.name); 126 | ok &= ring.quit(); 127 | } 128 | ring.name = ""; 129 | ring.init = 0; 130 | ring.quit = 0; 131 | } 132 | return ok; 133 | } 134 | 135 | inline bool alive() { 136 | auto &is_exiting = singleton::get().is_exiting; 137 | return is_exiting ? false : true; 138 | } 139 | 140 | inline bool reboot( bool display, int lvl ) { 141 | quit( display, lvl ); 142 | init( display, lvl, 256 ); 143 | return true; 144 | } 145 | } 146 | 147 | #ifdef FRODO_BUILD_DEMO 148 | #include 149 | #include 150 | 151 | namespace memory { 152 | bool init() { 153 | std::cout << "[ OK ] mem setup" << std::endl; 154 | return true; 155 | } 156 | bool quit() { 157 | std::cout << "[ OK ] mem teardown" << std::endl; 158 | return true; 159 | } 160 | } 161 | 162 | namespace logger { 163 | bool init() { 164 | std::cout << "[ OK ] logger setup" << std::endl; 165 | return true; 166 | } 167 | bool quit() { 168 | std::cout << "[ OK ] logger teardown" << std::endl; 169 | return true; 170 | } 171 | } 172 | 173 | namespace console { 174 | bool init() { 175 | std::cout << "[ OK ] console setup" << std::endl; 176 | return true; 177 | } 178 | bool quit() { 179 | std::cout << "[ OK ] console teardown" << std::endl; 180 | return true; 181 | } 182 | } 183 | 184 | int main() { 185 | 186 | // extra tests 187 | puts("-0- {"); 188 | frodo::init(true); 189 | frodo::quit(true); 190 | puts("-0- }"); 191 | 192 | puts("-1- {"); 193 | 194 | // app-defined levels 195 | // 00 memory and hooks 196 | frodo::ring( 0, { "memory", memory::init, memory::quit } ); 197 | // 10 subsystems 198 | frodo::ring( 13, { "logger", logger::init, logger::quit } ); 199 | frodo::ring( 14, { "console", console::init, console::quit } ); 200 | // 20 devices 201 | //frodo::ring( 20, { "audio", audio::init, audio::quit } ); 202 | //frodo::ring( 25, { "data", data::init, data::quit } ); 203 | //frodo::ring( 27, { "input", input::init, input::quit } ); 204 | // 30 opengl 205 | //frodo::ring( 34, { "ui", ui::init, ui::quit } ); 206 | //frodo::ring( 30, { "opengl", gl::init, gl::quit } ); 207 | //frodo::ring( 31, { "monitor", monitor::init, monitor::quit } ); 208 | //frodo::ring( 35, { "font", font::init, font::quit } ); 209 | // 40 game 210 | //frodo::ring( 40, { "model", model::init, model::quit } ); 211 | //frodo::ring( 45, { "world", world::init, world::quit } ); 212 | // 50 ui 213 | //frodo::ring( 59, { "help", help::init, help::quit } ); 214 | 215 | if( frodo::init(true) ) { 216 | 217 | puts("-2- {"); 218 | frodo::reboot(true, 13); 219 | puts("-2- }"); 220 | 221 | 222 | // app starts here 223 | std::string dummy; 224 | std::cout << "Press any key to continue... (try aborting or killing app too)" << std::endl; 225 | std::getline( std::cin, dummy ); 226 | 227 | // shutdown 228 | if( frodo::quit(true) ) { 229 | // ok 230 | //return 0; 231 | } 232 | } 233 | 234 | puts("-1- }"); 235 | 236 | // extra tests 237 | puts("-3- {"); 238 | frodo::init(true); 239 | frodo::quit(true); 240 | puts("-3- }"); 241 | 242 | #if FRODO_INSTALL_SIGHANDLERS 243 | // non-quit test 244 | puts("-4- {"); 245 | frodo::init(true); 246 | puts("-4- }"); 247 | #endif 248 | 249 | return -1; 250 | } 251 | #endif 252 | --------------------------------------------------------------------------------