Wed, 16 Jul 2014 18:57:48 +0300
- added a code generator to produce a configuration.inc which replaces the old, DIABOLIC configuration indexing mechanism
CMakeLists.txt | file | annotate | diff | comparison | revisions | |
codegen/CMakeLists.txt | file | annotate | diff | comparison | revisions | |
codegen/codegen.cpp | file | annotate | diff | comparison | revisions | |
src/configuration.cc | file | annotate | diff | comparison | revisions | |
src/configuration.h | file | annotate | diff | comparison | revisions | |
src/main.cc | file | annotate | diff | comparison | revisions |
--- a/CMakeLists.txt Wed Jul 16 15:00:41 2014 +0300 +++ b/CMakeLists.txt Wed Jul 16 18:57:48 2014 +0300 @@ -1,10 +1,13 @@ ###################################################################### -# CMake file for LDForge. -# The original QMake file was created on Sat Sep 22 17:29:49 2012 +# +# CMake file for LDForge. +# The original QMake file was created on Sat Sep 22 17:29:49 2012 +# ###################################################################### project (ldforge) add_subdirectory (updaterevision) +add_subdirectory (codegen) cmake_minimum_required (VERSION 2.6) find_package (Qt4 REQUIRED) find_package (OpenGL REQUIRED) @@ -12,11 +15,12 @@ option (TRANSPARENT_DIRECT_COLORS "Enables non-standard transparent direct colors" OFF) get_target_property (UPDATEREVISION_EXE updaterevision LOCATION) +get_target_property (CODEGEN_EXE codegen LOCATION) add_custom_target (revision_check ALL - COMMAND ${UPDATEREVISION_EXE} ${CMAKE_SOURCE_DIR} hginfo.h - WORKING_DIRECTORY ${CMAKE_BINARY_DIR} - DEPENDS updaterevision) + COMMAND ${UPDATEREVISION_EXE} ${CMAKE_SOURCE_DIR} hginfo.h + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} + DEPENDS updaterevision) include_directories (${QT_INCLUDES} ${CMAKE_CURRENT_BINARY_DIR}) @@ -116,6 +120,11 @@ ui/ytruder.ui ) +add_custom_target (codegeneration ALL + COMMAND ${CODEGEN_EXE} ${LDFORGE_SOURCES} ${CMAKE_BINARY_DIR}/configuration.inc + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + DEPENDS codegen) + set (LDFORGE_RESOURCES ldforge.qrc) set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x -W -Wall") set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lGLU") @@ -159,5 +168,5 @@ ${OPENGL_LIBRARIES} ) -add_dependencies (ldforge revision_check) +add_dependencies (ldforge revision_check codegeneration) install (TARGETS ldforge RUNTIME DESTINATION bin)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/codegen/CMakeLists.txt Wed Jul 16 18:57:48 2014 +0300 @@ -0,0 +1,2 @@ +cmake_minimum_required (VERSION 2.4) +add_executable (codegen codegen.cpp)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/codegen/codegen.cpp Wed Jul 16 18:57:48 2014 +0300 @@ -0,0 +1,176 @@ +/* + * LDForge: LDraw parts authoring CAD + * Copyright (C) 2013, 2014 Teemu Piippo + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <sstream> +#include <fstream> +#include <string> +#include <cstring> +#include <iostream> +#include <vector> +#include <algorithm> + +using std::string; +using std::vector; +using std::ifstream; +using std::size_t; +using std::strncmp; +using std::getline; +using std::cout; +using std::endl; + +struct entry_type +{ + string name; + string type; + string defvalue; + + inline bool operator< (entry_type const& other) const + { + return name < other.name; + } + + inline bool operator== (entry_type const& other) const + { + return name == other.name and type == other.type; + } + + inline bool operator!= (entry_type const& other) const + { + return not operator== (other); + } +}; + +char advance_pointer (char const*& ptr) +{ + char a = *ptr++; + if (*ptr == '\0') + throw false; + + return a; +} + +void read_cfgentries (string const& filename, vector<entry_type>& entries, const char* macroname) +{ + ifstream is (filename.c_str()); + string line; + size_t const macrolen = strlen (macroname); + + while (getline (is, line)) + { + try + { + if (strncmp (line.c_str(), macroname, macrolen) != 0) + continue; + + char const* ptr = &line[macrolen]; + entry_type entry; + + // Skip to paren + while (*ptr != '(') + advance_pointer (ptr); + + // Skip whitespace + while (isspace (*ptr)) + advance_pointer (ptr); + + // Skip paren + advance_pointer (ptr); + + // Read type + while (*ptr != ',') + entry.type += advance_pointer (ptr); + + // Skip comma and whitespace + for (advance_pointer (ptr); isspace (*ptr); advance_pointer (ptr)) + ; + + // Read name + while (*ptr != ',') + entry.name += advance_pointer (ptr); + + // Skip comma and whitespace + for (advance_pointer (ptr); isspace (*ptr); advance_pointer (ptr)) + ; + + // Read default + while (*ptr != ')') + entry.defvalue += advance_pointer (ptr); + + entries.push_back (entry); + } + catch (bool) {} + } +} + +bool check_equality (vector<entry_type> a, vector<entry_type> b) +{ + if (a.size() != b.size()) + return false; + + std::sort (a.begin(), a.end()); + std::sort (b.begin(), b.end()); + + for (size_t i = 0; i < a.size(); ++i) + { + if (a[i] != b[i]) + return false; + } + + return true; +} + +int main (int argc, char* argv[]) +{ + vector<entry_type> entries; + vector<entry_type> oldentries; + read_cfgentries (argv[argc - 1], oldentries, "CODEGEN_CACHE"); + + for (int arg = 1; arg < argc - 1; ++arg) + read_cfgentries (argv[arg], entries, "CFGENTRY"); + + if (check_equality (entries, oldentries)) + { + cout << "Configuration options unchanged" << endl; + } + else + { + std::ofstream os (argv[argc - 1]); + os << "#pragma once" << endl; + os << "#define CODEGEN_CACHE(A,B,C)" << endl; + + for (vector<entry_type>::const_iterator it = entries.begin(); it != entries.end(); ++it) + os << "CODEGEN_CACHE (" << it->type << ", " << it->name << ", " << it->defvalue << ")" << endl; + + os << endl; + for (vector<entry_type>::const_iterator it = entries.begin(); it != entries.end(); ++it) + os << "EXTERN_CFGENTRY (" << it->type << ", " << it->name << ")" << endl; + + os << endl; + os << "static void initConfigurationEntry (ConfigEntry* entry);" << endl; + os << "static void setupConfigurationLists()" << endl; + os << "{" << endl; + + for (vector<entry_type>::const_iterator it = entries.begin(); it != entries.end(); ++it) + os << "\tinitConfigurationEntry (new " << it->type << "ConfigEntry (&cfg::" << it->name << ", \"" << it->name << "\", " << it->defvalue << "));" << endl; + os << "}" << endl; + + cout << "Wrote configuration options list to " << argv[argc - 1] << "." << endl; + } + + return 0; +}
--- a/src/configuration.cc Wed Jul 16 15:00:41 2014 +0300 +++ b/src/configuration.cc Wed Jul 16 18:57:48 2014 +0300 @@ -31,6 +31,8 @@ #include "miscallenous.h" #include "mainWindow.h" #include "ldDocument.h" +#include "glRenderer.h" +#include "configuration.inc" #ifdef _WIN32 # define EXTENSION ".ini" @@ -40,31 +42,39 @@ #define MAX_CONFIG 512 -ConfigEntry* g_configPointers[MAX_CONFIG]; -static int g_cfgPointerCursor = 0; static QMap<QString, ConfigEntry*> g_configsByName; static QList<ConfigEntry*> g_configs; ConfigEntry::ConfigEntry (QString name) : m_name (name) {} +void Config::init() +{ + setupConfigurationLists(); + print ("Configuration initialized with %1 entries\n", g_configs.size()); +} + +static void initConfigurationEntry (ConfigEntry* entry) +{ + g_configs << entry; + g_configsByName[entry->name()] = entry; +} + // // Load the configuration from file // bool Config::load() { QSettings* settings = settingsObject(); - print ("config::load: Loading configuration file from %1\n", settings->fileName()); + print ("Loading configuration file from %1\n", settings->fileName()); - for (ConfigEntry* cfg : g_configPointers) + for (ConfigEntry* cfg : g_configs) { if (cfg == null) break; QVariant val = settings->value (cfg->name(), cfg->getDefaultAsVariant()); cfg->loadFromVariant (val); - g_configsByName[cfg->name()] = cfg; - g_configs << cfg; } if (g_win != null) @@ -135,20 +145,6 @@ return new QSettings (path, QSettings::IniFormat); } -// -// We cannot just add config objects to a list or vector because that would rely -// on the vector's c-tor being called before the configs' c-tors. With global -// variables we cannot assume that, therefore we need to use a C-style array here. -// -void ConfigEntry::addToArray (ConfigEntry* ptr) -{ - if (g_cfgPointerCursor == 0) - memset (g_configPointers, 0, sizeof g_configPointers); - - assert (g_cfgPointerCursor < MAX_CONFIG); - g_configPointers[g_cfgPointerCursor++] = ptr; -} - template<typename T> T* getConfigByName (QString name, ConfigEntry::Type type) {
--- a/src/configuration.h Wed Jul 16 15:00:41 2014 +0300 +++ b/src/configuration.h Wed Jul 16 18:57:48 2014 +0300 @@ -25,21 +25,12 @@ class QSettings; -#define CFGENTRY(T, NAME, DEFAULT) \ - namespace cfg \ - { \ - ConfigEntry::T##Type NAME; \ - T##ConfigEntry config_##NAME (&NAME, #NAME, DEFAULT); \ - } - -#define EXTERN_CFGENTRY(T, NAME) \ - namespace cfg \ - { \ - extern ConfigEntry::T##Type NAME; \ - } +#define CFGENTRY(T, NAME, DEFAULT) namespace cfg { ConfigEntry::T##Type NAME; } +#define EXTERN_CFGENTRY(T, NAME) namespace cfg { extern ConfigEntry::T##Type NAME; } namespace Config { + void init(); bool load(); bool save(); void reset(); @@ -80,10 +71,6 @@ virtual void loadFromVariant (const QVariant& val) = 0; virtual void resetValue() = 0; virtual QVariant toVariant() const = 0; - - -protected: - static void addToArray (ConfigEntry* ptr); }; // ============================================================================= @@ -96,7 +83,6 @@ m_valueptr (valueptr), \ m_default (def) \ { \ - ConfigEntry::addToArray (this); \ *m_valueptr = def; \ } \ \