Tue, 14 Jun 2022 19:50:31 +0300
Replace config collector with a simpler system
--- a/CMakeLists.txt Tue Jun 14 17:55:50 2022 +0300 +++ b/CMakeLists.txt Tue Jun 14 19:50:31 2022 +0300 @@ -70,6 +70,9 @@ src/parser.h src/polygoncache.h src/ring.h + src/settings.h + src/settingdefs.h + src/typeconversions.h src/uiutilities.h src/version.h src/vertexmap.h @@ -112,7 +115,6 @@ ) set (LDFORGE_OTHER_FILES - src/configurationoptions.txt ) set(LDFORGE_RESOURCES ldforge.qrc) @@ -157,7 +159,6 @@ ${QM_FILES} ${LDFORGE_FORMS_HEADERS} ${LDFORGE_OTHER_FILES} - ${CMAKE_BINARY_DIR}/configuration.cpp ) set_source_files_properties(${LDFORGE_HEADERS} PROPERTIES HEADER_FILE_ONLY TRUE) @@ -168,19 +169,6 @@ add_dependencies(ldforge resources) #cotire(ldforge) -add_custom_target (config_collection ALL - COMMAND python3 - "${CMAKE_SOURCE_DIR}/tools/configcollector.py" - --header ${CMAKE_BINARY_DIR}/configuration.h - --source ${CMAKE_BINARY_DIR}/configuration.cpp - --sourcedir ${CMAKE_SOURCE_DIR}/src - ${CMAKE_SOURCE_DIR}/src/configurationoptions.txt - WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) -add_dependencies(ldforge config_collection) -set_source_files_properties (${CMAKE_BINARY_DIR}/configuration.cpp PROPERTIES GENERATED TRUE) -set_property(SOURCE configuration.cpp PROPERTY SKIP_AUTOGEN ON) - - add_custom_target(linelength ALL COMMAND python3 "${CMAKE_SOURCE_DIR}/tools/linelength.py"
--- a/src/basics.h Tue Jun 14 17:55:50 2022 +0300 +++ b/src/basics.h Tue Jun 14 19:50:31 2022 +0300 @@ -37,6 +37,7 @@ #include <glm/glm.hpp> #include "geometry.h" #include "functional.h" +#include "typeconversions.h" template<typename T> using opt = std::optional<T>; @@ -51,46 +52,6 @@ return N; } -//! \brief casts @c x to a suitable unsigned integer -template<typename T> -constexpr auto unsigned_cast(T x) - -> std::enable_if_t<std::is_integral_v<T>, std::make_unsigned_t<T>> -{ - return static_cast<std::make_unsigned_t<T>>(x); -} - -//! \brief casts @c x to a suitable signed integer -template<typename T> -constexpr auto signed_cast(T x) - -> std::enable_if_t<std::is_integral_v<T>, std::make_signed_t<T>> -{ - return static_cast<std::make_signed_t<T>>(x); -} - -//! \brief casts floating point values to float -template<typename T> -constexpr auto float_cast(T x) - -> std::enable_if_t<std::is_floating_point_v<T>, float> -{ - return static_cast<float>(x); -} - -//! \brief casts floating point values to double -template<typename T> -constexpr auto double_cast(T x) - -> std::enable_if_t<std::is_floating_point_v<T>, double> -{ - return static_cast<double>(x); -} - -//! \brief casts floating point values to qreal -template<typename T> -constexpr auto qreal_cast(T x) - -> std::enable_if_t<std::is_floating_point_v<T>, qreal> -{ - return static_cast<qreal>(x); -} - template<int N, typename T, glm::qualifier Q> constexpr QPointF toQPointF(const glm::vec<N, T, Q>& vec) {
--- a/src/configurationoptions.txt Tue Jun 14 17:55:50 2022 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,26 +0,0 @@ -# -# LDForge configuration option definitions -# -# Syntax: -# option OptionName = value -# option OptionName = type {value} -# # comment -# - -# Rendering options -option Locale = "system" -option BackgroundColor = QColor{48, 48, 48} -option MainColor = QColor{255, 255, 64} -option SelectedColor = QColor{32, 32, 224} -option LineThickness = 2.0f -option LineAntiAliasing = true -option RenderStyle = 0 -option DrawWireframe = false -option DrawAxes = true -option MainWindowGeometry = QByteArray{} -option MainSplitterState = QByteArray{} -option RecentFiles = QStringList{} -option ViewMode = 1 - -# File management options -option Libraries = QVector<Library>{}
--- a/src/gl/common.h Tue Jun 14 17:55:50 2022 +0300 +++ b/src/gl/common.h Tue Jun 14 19:50:31 2022 +0300 @@ -135,3 +135,15 @@ bool drawAxes = true; }; } + +Q_DECLARE_METATYPE(gl::RenderStyle) + +inline QDataStream &operator<<(QDataStream& stream, const gl::RenderStyle style) +{ + return stream << enum_value_cast(style); +} + +inline QDataStream &operator>>(QDataStream& stream, gl::RenderStyle& style) +{ + return stream >> enum_value_cast(style); +}
--- a/src/libraries.cpp Tue Jun 14 17:55:50 2022 +0300 +++ b/src/libraries.cpp Tue Jun 14 19:50:31 2022 +0300 @@ -18,7 +18,7 @@ #include <QSettings> #include "libraries.h" -#include "configuration.h" +#include "settings.h" /** * @brief Constructs a new library manager @@ -30,17 +30,6 @@ } /** - * @brief Constructs a library manager from settings - * @param settings Settings to construct from - * @param parent Parent object - */ -LibraryManager::LibraryManager(Configuration* settings, QObject* parent) : - QAbstractTableModel{parent} -{ - this->restoreFromSettings(settings); -} - -/** * @brief Yields a begin-terator for the libraries * @return iterator */ @@ -161,18 +150,18 @@ * changes are lost. * @param settings Settings object to restore from. */ -void LibraryManager::restoreFromSettings(Configuration* settings) +void LibraryManager::restoreFromSettings() { - this->libraries = settings->libraries(); + this->libraries = setting<Setting::Libraries>(); } /** * @brief Saves the libraries to the specified settings object. * @param settings Settings object to modify. */ -void LibraryManager::storeToSettings(Configuration* settings) +void LibraryManager::storeToSettings() { - settings->setLibraries(this->libraries); + setSetting<Setting::Libraries>(this->libraries); } /**
--- a/src/libraries.h Tue Jun 14 17:55:50 2022 +0300 +++ b/src/libraries.h Tue Jun 14 19:50:31 2022 +0300 @@ -55,7 +55,6 @@ Q_OBJECT public: LibraryManager(QObject* parent = nullptr); - LibraryManager(Configuration* settings, QObject* parent = nullptr); QVector<Library>::const_iterator begin() const; QVector<Library>::const_iterator end() const; QString findFile(QString fileName) const; @@ -64,8 +63,8 @@ const Library& library(int libraryIndex) const; void setLibraryPath(int libraryIndex, const QDir& path); void setLibraryRole(int libraryIndex, const Library::Role role); - void restoreFromSettings(Configuration* settings); - void storeToSettings(Configuration* settings); + void restoreFromSettings(); + void storeToSettings(); int count() const; void moveLibrary(const int libraryFromIndex, const int libraryToIndex); // Definitions for QAbstractTableModel
--- a/src/main.cpp Tue Jun 14 17:55:50 2022 +0300 +++ b/src/main.cpp Tue Jun 14 19:50:31 2022 +0300 @@ -13,6 +13,7 @@ #include "document.h" #include "settingseditor/settingseditor.h" #include "widgets/colorselectdialog.h" +#include "settings.h" static const QDir LOCALE_DIR {":/locale"}; @@ -54,6 +55,8 @@ QCoreApplication::setOrganizationDomain("hecknology.net"); qRegisterMetaTypeStreamOperators<Library>("Library"); qRegisterMetaTypeStreamOperators<Libraries>("Libraries"); + qRegisterMetaTypeStreamOperators<gl::RenderStyle>(); + qRegisterMetaTypeStreamOperators<QMdiArea::ViewMode>(); } template<typename BaseType, typename MemberType, typename DataType> @@ -251,16 +254,16 @@ ui->actionDrawAxes->setChecked(renderPreferences->drawAxes); }; -static gl::RenderPreferences loadRenderPreferences(Configuration* settings) +static gl::RenderPreferences loadRenderPreferences() { return gl::RenderPreferences{ - .style = static_cast<gl::RenderStyle>(settings->renderStyle()), - .mainColor = settings->mainColor(), - .backgroundColor = settings->backgroundColor(), - .selectedColor = settings->selectedColor(), - .lineThickness = settings->lineThickness(), - .lineAntiAliasing = settings->lineAntiAliasing(), - .drawAxes = settings->drawAxes(), + .style = setting<Setting::RenderStyle>(), + .mainColor = setting<Setting::MainColor>(), + .backgroundColor = setting<Setting::BackgroundColor>(), + .selectedColor = setting<Setting::SelectedColor>(), + .lineThickness = setting<Setting::LineThickness>(), + .lineAntiAliasing = setting<Setting::LineAntiAliasing>(), + .drawAxes = setting<Setting::DrawAxes>(), }; } @@ -327,9 +330,7 @@ DocumentManager documents; QString currentLanguage = "en"; QTranslator translator{&mainWindow}; - Configuration settings; LibraryManager libraries{&mainWindow}; - QByteArray documentSplitterState; QStringList recentlyOpenedFiles; ColorTable colorTable; gl::RenderPreferences renderPreferences; @@ -337,12 +338,12 @@ const uiutilities::KeySequenceMap defaultKeyboardShortcuts = uiutilities::makeKeySequenceMap(uiutilities::collectActions(&mainWindow)); const auto saveSettings = [&]{ - settings.setMainWindowGeometry(mainWindow.saveGeometry()); - settings.setRecentFiles(recentlyOpenedFiles); - settings.setMainSplitterState(documentSplitterState); - settings.setRenderStyle(static_cast<int>(renderPreferences.style)); - settings.setDrawAxes(renderPreferences.drawAxes); - libraries.storeToSettings(&settings); + setSetting<Setting::MainWindowGeometry>(mainWindow.saveGeometry()); + setSetting<Setting::RecentFiles>(recentlyOpenedFiles); + setSetting<Setting::RenderStyle>(renderPreferences.style); + setSetting<Setting::DrawAxes>(renderPreferences.drawAxes); + setSetting<Setting::DrawAxes>("hello world"); + libraries.storeToSettings(); }; const auto updateRecentlyOpenedDocumentsMenu = [&]{ rebuildRecentFilesMenu(ui.menuRecentFiles, recentlyOpenedFiles, &mainWindow); @@ -370,15 +371,14 @@ }, action); }; const auto restoreSettings = [&]{ - recentlyOpenedFiles = settings.recentFiles(); - documentSplitterState = settings.mainSplitterState(); - renderPreferences = loadRenderPreferences(&settings); - changeLanguage(settings.locale(), &translator); - libraries.restoreFromSettings(&settings); + recentlyOpenedFiles = setting<Setting::RecentFiles>(); + renderPreferences = loadRenderPreferences(); + changeLanguage(setting<Setting::Locale>(), &translator); + libraries.restoreFromSettings(); updateRecentlyOpenedDocumentsMenu(); colorTable = loadColors(&libraries); updateRenderPreferences(&ui, &renderPreferences, &documents); - ui.mdiArea->setViewMode(static_cast<QMdiArea::ViewMode>(settings.viewMode())); + ui.mdiArea->setViewMode(setting<Setting::ViewMode>()); ui.retranslateUi(&mainWindow); }; const auto addRecentlyOpenedFile = [&](const QString& path){ @@ -407,7 +407,7 @@ data->tools->setGridMatrix(XZ); data->model = model; data->canvas->addRenderLayer(data->axesLayer.get()); - data->canvas->setLayerEnabled(data->axesLayer.get(), settings.drawAxes()); + data->canvas->setLayerEnabled(data->axesLayer.get(), setting<Setting::DrawAxes>()); data->canvas->addRenderLayer(data->gridLayer.get()); data->canvas->addRenderLayer(data->tools.get()); documents.setModelPayload(modelId, data); @@ -476,7 +476,7 @@ } }); QObject::connect(ui.actionSettingsEditor, &QAction::triggered, [&]{ - SettingsEditor settingsEditor{&settings, defaultKeyboardShortcuts, &mainWindow}; + SettingsEditor settingsEditor{defaultKeyboardShortcuts, &mainWindow}; const int result = settingsEditor.exec(); if (result == QDialog::Accepted) { @@ -600,7 +600,7 @@ } }); mainWindow.setWindowTitle(title()); - mainWindow.restoreGeometry(settings.mainWindowGeometry()); + mainWindow.restoreGeometry(setting<Setting::MainWindowGeometry>()); restoreSettings(); updateRenderPreferences(&ui, &renderPreferences, &documents); mainWindow.show();
--- a/src/model.h Tue Jun 14 17:55:50 2022 +0300 +++ b/src/model.h Tue Jun 14 19:50:31 2022 +0300 @@ -47,36 +47,6 @@ struct Empty {}; -template<typename T, typename R> -struct transfer_reference -{ - using type = std::remove_reference_t<R>; -}; - -template<typename T, typename R> -struct transfer_reference<T&, R> -{ - using type = std::remove_reference_t<R>&; -}; - -template<typename T, typename R> -struct transfer_reference<const T&, R> -{ - using type = const std::remove_reference_t<R>&; -}; - -template<typename T, typename R> -struct transfer_reference<T&&, R> -{ - using type = std::remove_reference_t<R>&&; -}; - -template<typename T, typename R> -using transfer_reference_t = typename transfer_reference<T, R>::type; -static_assert(std::is_same_v<transfer_reference_t<int, char>, char>); -static_assert(std::is_same_v<transfer_reference_t<int&, char>, char&>); -static_assert(std::is_same_v<transfer_reference_t<int&&, char>, char&&>); - using ModelElement = std::variant< Colored<SubfileReference>, Colored<LineSegment>, @@ -142,25 +112,25 @@ constexpr void visitPoints(Fn&& func, T&& element) { visitPolygon<void>( - [&func](transfer_reference_t<T&&, LineSegment> edge) + [&func](transfer_cvref_t<T&&, LineSegment> edge) { func(edge.p1); func(edge.p2); }, - [&func](transfer_reference_t<T&&, Triangle>& tri) + [&func](transfer_cvref_t<T&&, Triangle>& tri) { func(tri.p1); func(tri.p2); func(tri.p3); }, - [&func](transfer_reference_t<T&&, Quadrilateral>& quad) + [&func](transfer_cvref_t<T&&, Quadrilateral>& quad) { func(quad.p1); func(quad.p2); func(quad.p3); func(quad.p4); }, - [&func](transfer_reference_t<T&&, ConditionalEdge>& cedge) + [&func](transfer_cvref_t<T&&, ConditionalEdge>& cedge) { func(cedge.p1); func(cedge.p2);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/settingdefs.h Tue Jun 14 19:50:31 2022 +0300 @@ -0,0 +1,33 @@ +#include <QMdiArea> +#include "libraries.h" +#include "gl/common.h" + +#ifndef SETTING +enum class Setting { +# define SETTING(NAME, DEFVALUE) NAME, +# define AUTOMATIC_SETTINGS_DEF +#endif + +// Rendering options +SETTING(Locale, QString{"system"}) +SETTING(BackgroundColor, (QColor{48, 48, 48})) +SETTING(MainColor, (QColor{255, 255, 64})) +SETTING(SelectedColor, (QColor{32, 32, 224})) +SETTING(LineThickness, 2.0f) +SETTING(LineAntiAliasing, true) +SETTING(RenderStyle, gl::RenderStyle::Normal) +SETTING(DrawWireframe, false) +SETTING(DrawAxes, true) +SETTING(MainWindowGeometry, QByteArray{}) +SETTING(MainSplitterState, QByteArray{}) +SETTING(RecentFiles, QStringList{}) +SETTING(ViewMode, QMdiArea::TabbedView) + +// File management options +SETTING(Libraries, QVector<Library>{}) + +#ifdef AUTOMATIC_SETTINGS_DEF +}; +# undef SETTING +# undef AUTOMATIC_SETTINGS_DEF +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/settings.h Tue Jun 14 19:50:31 2022 +0300 @@ -0,0 +1,55 @@ +#ifndef SETTINGS_H +#define SETTINGS_H +#include <QSettings> +#include "settingdefs.h" + +// settingdefs - get name and default value by setting +static const struct Settingdef +{ + const char* name; + QVariant defaultValue; +} settingdefs[] = { +#define SETTING(NAME, DEFVALUE) {#NAME, QVariant::fromValue(DEFVALUE)}, +#include "settingdefs.h" +#undef SETTING +}; + +// SettingType - get type of setting by enumerator +template<Setting setting> +struct SettingType{}; + +#define SETTING(NAME, DEFVALUE) \ + template<> \ + struct SettingType<Setting::NAME> \ + { \ + using type = decltype((DEFVALUE)); \ + }; +#include "settingdefs.h" +#undef SETTING + +template<Setting setting> +using SettingType_t = typename SettingType<setting>::type; +static_assert(std::is_same_v<SettingType_t<Setting::DrawAxes>, bool>); + +// get() - get setting by enumerator +template<Setting X> +inline SettingType_t<X> setting() +{ + const Settingdef& def = settingdefs[static_cast<int>(X)]; + return QSettings{}.value(def.name, def.defaultValue).value<SettingType_t<X>>(); +} + +// get() - get setting by enumerator +template<Setting X> +inline void setSetting(const SettingType_t<X>& value) +{ + const Settingdef& def = settingdefs[static_cast<int>(X)]; + if (value == def.defaultValue.value<SettingType_t<X>>()) { + QSettings{}.remove(def.name); + } + else { + QSettings{}.setValue(def.name, QVariant::fromValue(value)); + } +} + +#endif // SETTINGS_H
--- a/src/settingseditor/librarieseditor.cpp Tue Jun 14 17:55:50 2022 +0300 +++ b/src/settingseditor/librarieseditor.cpp Tue Jun 14 19:50:31 2022 +0300 @@ -4,9 +4,9 @@ #include "librarieseditor.h" #include "ui_librarieseditor.h" -LibrariesEditor::LibrariesEditor(Configuration* settings, QWidget* parent) : +LibrariesEditor::LibrariesEditor(QWidget* parent) : QWidget{parent}, - libraries{settings, this}, + libraries{this}, ui{*new Ui_LibrariesEditor} { this->ui.setupUi(this); @@ -144,7 +144,7 @@ return this->ui.librariesTable->selectionModel()->currentIndex().row(); } -void LibrariesEditor::saveSettings(Configuration* settings) +void LibrariesEditor::saveSettings() { - this->libraries.storeToSettings(settings); + this->libraries.storeToSettings(); }
--- a/src/settingseditor/librarieseditor.h Tue Jun 14 17:55:50 2022 +0300 +++ b/src/settingseditor/librarieseditor.h Tue Jun 14 19:50:31 2022 +0300 @@ -7,9 +7,9 @@ { Q_OBJECT public: - LibrariesEditor(Configuration* settings, QWidget* parent = nullptr); + LibrariesEditor(QWidget* parent = nullptr); ~LibrariesEditor(); - void saveSettings(Configuration* settings); + void saveSettings(); private Q_SLOTS: void searchPathForNewLibrary(); void addNewLibrary();
--- a/src/settingseditor/settingseditor.cpp Tue Jun 14 17:55:50 2022 +0300 +++ b/src/settingseditor/settingseditor.cpp Tue Jun 14 19:50:31 2022 +0300 @@ -1,20 +1,19 @@ #include <QSettings> #include <QMdiArea> +#include "settings.h" #include "gl/common.h" #include "keyboardshortcutseditor.h" #include "settingseditor.h" #include "ui_settingseditor.h" SettingsEditor::SettingsEditor( - Configuration* settings, const uiutilities::KeySequenceMap& defaultKeyboardShortcuts, QWidget* parent ) : QDialog{parent}, ui{*new Ui_SettingsEditor}, - settings{settings}, - libraries{settings, this}, - librariesEditor{settings, this}, + libraries{this}, + librariesEditor{this}, defaultKeyboardShortcuts{defaultKeyboardShortcuts} { this->ui.setupUi(this); @@ -40,17 +39,17 @@ void SettingsEditor::handleAccepted() { - this->settings->setLocale(this->ui.language->currentData().toString()); - this->settings->setMainColor(this->ui.mainColorButton->selectedColor()); - this->settings->setBackgroundColor(this->ui.backgroundColorButton->selectedColor()); - this->settings->setSelectedColor(this->ui.selectedColorButton->selectedColor()); - this->settings->setLineThickness(static_cast<GLfloat>(this->ui.lineThickness->value())); - this->settings->setLineAntiAliasing(this->ui.lineAntiAliasing->isChecked()); + setSetting<Setting::Locale>(this->ui.language->currentData().toString()); + setSetting<Setting::MainColor>(this->ui.mainColorButton->selectedColor()); + setSetting<Setting::BackgroundColor>(this->ui.backgroundColorButton->selectedColor()); + setSetting<Setting::SelectedColor>(this->ui.selectedColorButton->selectedColor()); + setSetting<Setting::LineThickness>(static_cast<GLfloat>(this->ui.lineThickness->value())); + setSetting<Setting::LineAntiAliasing>(this->ui.lineAntiAliasing->isChecked()); const int viewMode = this->ui.viewModeButtonGroup->checkedId(); if (viewMode != -1) { - this->settings->setViewMode(viewMode); + setSetting<Setting::ViewMode>(static_cast<QMdiArea::ViewMode>(viewMode)); } - this->librariesEditor.saveSettings(this->settings); + this->librariesEditor.saveSettings(); } void SettingsEditor::loadLocales() @@ -77,13 +76,13 @@ void SettingsEditor::setDefaults() { - this->setCurrentLanguage(this->settings->locale()); - this->ui.mainColorButton->setSelectedColor(this->settings->mainColor()); - this->ui.backgroundColorButton->setSelectedColor(this->settings->backgroundColor()); - this->ui.selectedColorButton->setSelectedColor(this->settings->selectedColor()); - this->ui.lineThickness->setValue(static_cast<double>(this->settings->lineThickness())); - this->ui.lineAntiAliasing->setChecked(this->settings->lineAntiAliasing()); - auto* const viewModeButton = this->ui.viewModeButtonGroup->button(this->settings->viewMode()); + this->setCurrentLanguage(setting<Setting::Locale>()); + this->ui.mainColorButton->setSelectedColor(setting<Setting::MainColor>()); + this->ui.backgroundColorButton->setSelectedColor(setting<Setting::BackgroundColor>()); + this->ui.selectedColorButton->setSelectedColor(setting<Setting::SelectedColor>()); + this->ui.lineThickness->setValue(double_cast(setting<Setting::LineThickness>())); + this->ui.lineAntiAliasing->setChecked(setting<Setting::LineAntiAliasing>()); + auto* const viewModeButton = this->ui.viewModeButtonGroup->button(setting<Setting::ViewMode>()); if (viewModeButton != nullptr) { viewModeButton->setChecked(true); }
--- a/src/settingseditor/settingseditor.h Tue Jun 14 17:55:50 2022 +0300 +++ b/src/settingseditor/settingseditor.h Tue Jun 14 19:50:31 2022 +0300 @@ -10,15 +10,13 @@ { Q_OBJECT public: - SettingsEditor(Configuration* settings, - const uiutilities::KeySequenceMap& defaultKeyboardShortcuts = {}, + SettingsEditor(const uiutilities::KeySequenceMap& defaultKeyboardShortcuts = {}, QWidget* parent = nullptr); ~SettingsEditor(); private Q_SLOTS: void handleAccepted(); private: class Ui_SettingsEditor& ui; - Configuration* const settings; LibraryManager libraries; LibrariesEditor librariesEditor; const uiutilities::KeySequenceMap defaultKeyboardShortcuts;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/typeconversions.h Tue Jun 14 19:50:31 2022 +0300 @@ -0,0 +1,88 @@ +#ifndef TYPECONVERSIONS_H +#define TYPECONVERSIONS_H +#include <type_traits> +#include <QtGlobal> + +template<typename T, typename R> +struct transfer_cvref +{ + using type = std::remove_reference_t<R>; +}; + +template<typename T, typename R> +struct transfer_cvref<T&, R> +{ + using type = std::remove_reference_t<R>&; +}; + +template<typename T, typename R> +struct transfer_cvref<const T&, R> +{ + using type = const std::remove_reference_t<R>&; +}; + +template<typename T, typename R> +struct transfer_cvref<T&&, R> +{ + using type = std::remove_reference_t<R>&&; +}; + +//! \brief transfer l-value reference, r-value reference and const l-value +//! reference from T onto R +template<typename T, typename R> +using transfer_cvref_t = typename transfer_cvref<T, R>::type; +static_assert(std::is_same_v<transfer_cvref_t<int, char>, char>); +static_assert(std::is_same_v<transfer_cvref_t<int&, char>, char&>); +static_assert(std::is_same_v<transfer_cvref_t<int&&, char>, char&&>); + +//! \brief casts @c x to a suitable unsigned integer +template<typename T> +constexpr auto unsigned_cast(T x) + -> std::enable_if_t<std::is_integral_v<T>, std::make_unsigned_t<T>> +{ + return static_cast<std::make_unsigned_t<T>>(x); +} + +//! \brief casts @c x to a suitable signed integer +template<typename T> +constexpr auto signed_cast(T x) + -> std::enable_if_t<std::is_integral_v<T>, std::make_signed_t<T>> +{ + return static_cast<std::make_signed_t<T>>(x); +} + +//! \brief casts floating point values to float +template<typename T> +constexpr auto float_cast(T x) + -> std::enable_if_t<std::is_floating_point_v<T>, float> +{ + return static_cast<float>(x); +} + +//! \brief casts floating point values to double +template<typename T> +constexpr auto double_cast(T x) + -> std::enable_if_t<std::is_floating_point_v<T>, double> +{ + return static_cast<double>(x); +} + +//! \brief casts floating point values to qreal +template<typename T> +constexpr auto qreal_cast(T x) + -> std::enable_if_t<std::is_floating_point_v<T>, qreal> +{ + return static_cast<qreal>(x); +} + +//! \brief convert an enum value to its corresponding integer value (including +//! references) +template<typename T> +constexpr auto&& enum_value_cast(T&& enu) +{ + using valuetype = std::underlying_type_t<std::remove_cvref_t<T>>; + using reftype = transfer_cvref_t<T&&, valuetype>; + return reinterpret_cast<reftype>(enu); +} + +#endif // TYPECONVERSIONS_H
--- a/tools/configcollector.py Tue Jun 14 17:55:50 2022 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,225 +0,0 @@ -#!/usr/bin/env python3 -# coding: utf-8 -# -# Copyright 2015 - 2017 Teemu Piippo -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# -# 1. Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# 3. Neither the name of the copyright holder nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED -# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A -# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER -# OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES(INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT(INCLUDING -# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -# - -from argparse import ArgumentParser -from collections import OrderedDict -import caseconversions -import outputfile - -# These types are passed by value -passbyvalue = {'int', 'bool', 'float', 'double', 'qreal'} - -def deduce_type(value): - ''' - Try to determine the type of value from the value itself. - ''' - if value in('true', 'false'): - return 'bool' - elif value.startswith(('"', 'R"')) and value.endswith('"'): - return 'QString' - - try: - int(value) - return 'int' - except: - pass - - try: - float(value) - return 'double' - except: - pass - - if value.endswith('f'): - try: - float(value[:-1]) - return 'float' - except: - pass - - raise ValueError('unable to deduce type of %r' % value) - -class ConfigCollector: - def __init__(self, args): - self.declarations = OrderedDict() - self.qtTypes = set() - self.args = args - - def collect(self, filename): - with open(filename) as file: - for linenumber, line in enumerate(file, 1): - try: - line = line.strip() - if line and not line.startswith('#'): - from re import search - match = search('^option (\w+) = (.+)$', line) - if not match: - raise ValueError('unable to parse: %r' % line) - name, value = match.groups() - match = search(r'^([a-zA-Z0-9_<>]+)\s*\{(.*)\}$', value) - try: - typename = match.group(1) - except: - typename = deduce_type(value) - self.declare(name, typename, value) - except ValueError as error: - from sys import stderr, exit - print(str.format( - '{file}:{line}: {error}', - file = filename, - line = linenumber, - error = str(error), - ), file = stderr) - exit(1) - # Sort the declarations in alphabetical order - self.declarations = OrderedDict(sorted(self.declarations.items(), key = lambda t: t[1]['name'])) - # Fill in additional information - for declaration in self.declarations.values(): - declaration['readgate'] = caseconversions.convert_case(declaration['name'], style='java') - declaration['writegate'] = 'set' + caseconversions.convert_case(declaration['name'], style='camel') - declaration['togglefunction'] = 'toggle' + caseconversions.convert_case(declaration['name'], style='camel') - if declaration['type'] in passbyvalue: - declaration['typereference'] = declaration['type'] - else: - declaration['typereference'] = 'const %s&' % declaration['type'] - - def declare(self, name, typename, default): - from re import findall - if name in self.declarations: - raise ValueError('Attempted to redeclare %r' % name) - self.declarations[name] = { - 'name': name, - 'type': typename, - 'default': default - } - # Keep a file of any Qt types, we'll need to #include them. - self.qtTypes.update(findall(r'Q\w+', typename)) - - def writeHeader(self, device): - device.write('#pragma once\n') - device.write('#include <QMap>\n') - device.write('#include <QSettings>\n') - device.write('#include <glm/glm.hpp>\n') - for qtType in sorted(self.qtTypes): - device.write('#include <%s>\n' % qtType) - device.write('#include "libraries.h"\n') - device.write('\n') - formatargs = {} - write = lambda value: device.write(value) - write('class Configuration : private QSettings\n') - write('{\n') - write('public:\n') - write('\tusing QSettings::QSettings;\n'); - write('\tbool exists(const QString& name);\n') - write('\tQVariant value(const QString& name);\n') - write('\tQVariant setValue(const QString& name);\n') - for declaration in self.declarations.values(): - write('\t{type} {readgate}();\n'.format(**declaration)) - for declaration in self.declarations.values(): - write('\tvoid {writegate}({typereference} value);\n'.format(**declaration)) - for declaration in filter(lambda declaration: declaration['type'] == 'bool', self.declarations.values()): - write('\tvoid {togglefunction}();\n'.format(**declaration)) - write('private:\n') - write('\tusing QSettings::value;\n') - write('\tusing QSettings::setValue;\n') - write('\tstatic const QMap<QString, QVariant>& defaults();\n') - write('};\n') - - def writeSource(self, device, headername): - device.write('#include <QSet>\n') - device.write('#include <QSettings>\n') - device.write('#include <QVariant>\n') - device.write('#include "%s/mainwindow.h"\n' % (self.args.sourcedir)) - device.write('#include "%s"\n' % headername) - device.write( - 'const QMap<QString, QVariant>& Configuration::defaults()\n' - '{\n' - '\tstatic const QMap<QString, QVariant> defaults {\n' - ) - for declaration in self.declarations.values(): - device.write('\t\t{{"{name}", QVariant::fromValue<{type}>({default})}},\n'.format(**declaration)) - device.write('\t};\n' - '\treturn defaults;\n' - '}\n' - '\n') - device.write('bool Configuration::exists(const QString& name)\n') - device.write('{\n') - device.write('\treturn defaults().contains(name);\n') - device.write('}\n') - device.write('\n') - device.write('QVariant Configuration::value(const QString& name)\n' - '{\n' - '\treturn this->value(name, Configuration::defaults().value(name));\n' - '}\n') - device.write('\n') - for declaration in self.declarations.values(): - device.write('{type} Configuration::{readgate}()\n'.format(**declaration)) - device.write('{\n') - device.write('\tstatic const QVariant defaultvalue = QVariant::fromValue<{type}>({default});\n'.format(**declaration)) - device.write('\treturn this->value("{name}", defaultvalue).value<{type}>();\n'.format(**declaration)) - device.write('}\n') - device.write('\n') - for declaration in self.declarations.values(): - device.write('void Configuration::{writegate}({typereference} value)\n'.format(**declaration)) - device.write('{\n') - device.write('\tif (value != {default})\n'.format(**declaration)) - device.write('\t\tthis->setValue("{name}", QVariant::fromValue<{type}>(value));\n'.format(**declaration)) - device.write('\telse\n') - device.write('\t\tthis->remove("{name}");\n'.format(**declaration)) - device.write('}\n') - device.write('\n') - for declaration in filter(lambda declaration: declaration['type'] == 'bool', self.declarations.values()): - device.write('void Configuration::{togglefunction}()\n'.format(**declaration)) - device.write('{\n') - device.write('\t{writegate}(not {readgate}());\n'.format(**declaration)) - device.write('}\n') - device.write('\n') - -def main(): - parser = ArgumentParser(description='Collects a list of configuration objects') - parser.add_argument('input') - parser.add_argument('--header', required=True) - parser.add_argument('--source', required=True) - parser.add_argument('--sourcedir', required=True) - args = parser.parse_args() - collector = ConfigCollector(args) - collector.collect(args.input) - from outputfile import OutputFile - header = OutputFile(args.header) - source = OutputFile(args.source) - collector.writeSource(source, headername=args.header) - collector.writeHeader(header) - header.save(verbose = True) - source.save(verbose = True) - -if __name__ == '__main__': - main()