- added a code generator to produce a configuration.inc which replaces the old, DIABOLIC configuration indexing mechanism

Wed, 16 Jul 2014 18:57:48 +0300

author
Teemu Piippo <crimsondusk64@gmail.com>
date
Wed, 16 Jul 2014 18:57:48 +0300
changeset 845
fec7023e91a7
parent 844
11587d419d2f
child 846
e4ad109f33c6

- 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;															\
 	}																				\
 																					\
--- a/src/main.cc	Wed Jul 16 15:00:41 2014 +0300
+++ b/src/main.cc	Wed Jul 16 18:57:48 2014 +0300
@@ -50,6 +50,7 @@
 	app.setOrganizationName (APPNAME);
 	app.setApplicationName (APPNAME);
 	initCrashCatcher();
+	Config::init();
 
 	// Load or create the configuration
 	if (not Config::load())

mercurial