Loading...
Smeshan avatar Smeshan 89 Точки

Четене на настройки от файл

Здравейте,

искам да направя настройките да се четат от файл.

Това което направих за сега:

MonitorConfig.txt

name of the display:Hardwear renderering
display height:800
display width:600


EngineConfigLoader.cpp (не е целия)

const char* MONITOR_CFG_FILE = "../sdl_utils/config/MonitorConfig.txt";

static std::string readConfigFromFile(const int32_t lineNum) {
    std::fstream file(MONITOR_CFG_FILE );
    if (!file.is_open()) {
        std::cerr << "Unable to open config file: " << MONITOR_CFG_FILE << std::endl;
    }
    std::string configLine;
    int32_t currLine = 0;

    while (!file.eof()) { // eof -> end of file
        std::string discard;

        if (currLine == lineNum) {
            getline(file, discard, ':'); //delete text before ':'
            getline(file, configLine);
            //std::cerr << "Line: " << configLine << std::endl;
            break;
        }
        getline(file, discard);
        //std::cerr << "Discard: " << discard << std::endl;
        currLine++;
    }
    return configLine;
}

static void populateMonitorConfig(MonitorConfig &outConfig) {
    outConfig.windowName = readConfigFromFile(0);
    outConfig.windowHeight = stoi(readConfigFromFile(1));
    outConfig.windowWidth = stoi(readConfigFromFile(2));
    outConfig.windowFlags = WINDOW_SHOWN;
}

И няколко неща не ми харесват или се чудя как да станат:
1. Като станат 100 настройки, while цикъла ще има да си върти докато стигне до 87-мия ред (примерно) за да го вземе. :Х И т. н..
2. Реда на настройките във файла трябва напълно да отговаря на реда, в който съм ги написал в populateMonitorConfig() (или поне номера на реда да отговаря). Това ми се струва бомба със закъснител и отделно трудно за промени..
3. populateMonitorConfig() стана адски нечетима.. :/

Ще продължа да го мисля и преработвам, но ще се радвам да се получи някаква дискусия и да съберем добри идеи :)

Поздрави,
Илиян

Тагове:
1
C++ Applications Development 17/10/2021 00:07:01
Smeshan avatar Smeshan 89 Точки

Оправих точка 3 с един enum. По-добре стана.

enum monitorConfig {
    WINDOW_NAME,
    DISPLAY_HEIGHT,
    DISPLAY_WIDTH
};

...

static void populateMonitorConfig(MonitorConfig &outConfig) {
    outConfig.windowName = readConfigFromFile(WINDOW_NAME);
    outConfig.windowHeight = stoi(readConfigFromFile(DISPLAY_HEIGHT));
    outConfig.windowWidth = stoi(readConfigFromFile(DISPLAY_WIDTH));
    outConfig.windowFlags = WINDOW_SHOWN;
}

 

1
Smeshan avatar Smeshan 89 Точки

И още подобрения:

enum monitorConfig {
    WINDOW_NAME,
    DISPLAY_HEIGHT,
    DISPLAY_WIDTH
};

const char *MONITOR_CFG_FILE = "../sdl_utils/config/MonitorConfig.txt";

static std::vector<std::string> readConfigsFromFile() {
    std::vector<std::string> configs;
    std::fstream file(MONITOR_CFG_FILE);
    if (!file.is_open()) {
        std::cerr << "Unable to open config file: " << MONITOR_CFG_FILE << std::endl;
    }
    std::string configLine;
    std::string discard;

    while (!file.eof()) { // eof -> end of file
        getline(file, discard, ':'); //delete text before ':'
        getline(file, configLine);
        configs.push_back(configLine);
    }
    return configs;
}

static void populateMonitorConfig(MonitorConfig &outConfig) {
    std::vector<std::string> configs = readConfigsFromFile();

    outConfig.windowName = configs[WINDOW_NAME];
    outConfig.windowHeight = stoi(configs[DISPLAY_HEIGHT]);
    outConfig.windowWidth = stoi(configs[DISPLAY_WIDTH]);
    outConfig.windowFlags = WINDOW_SHOWN;
}

И все пак, това е един начин да се направи. А и станаха много include-и или това не е проблем?
#include <fstream>
#include <iostream>
#include <string>
#include <vector>

0
17/10/2021 00:17:14
j.petrov_90 avatar j.petrov_90 373 Точки

Привет, Илиян,

Адмирации, че си правиш труда да работиш в тази посока!
За жалост, ако искаш функционалността да е четима, лесна за използване и трудна да правене на грешки - трябва да вложиш още усилия.

Главният проблем, е че във 1 файл се опитват да правиш 3 коренно различни неща:
- четене от файл следвайки някакъв стил (nameOfParam:value)
- валидация на прочетената ивформация
- попълване на информацията от файла в структурите на програмата ти.

Разбиий това в 3 отделни класа, които правят само по 1 нещо и си 6 :)
1) четене от файл - ще изчита информацията и ще я съхранява в удобна за ползване структура. Може да я сложиш в map/unordered_map, където за ключ имаш std::string (името на параметъра) и std::string за неговото value.
2) валидация на горната конфигурация - какво става ако ти попълня display width:pesho? std::stoi функцията в последствие би хвърлила exception
3) използваш новата си структура с валидирана информация да попълниш структурите от програмата си (това вече го имаш).
4) пиеш една бира за добре свършената работа

Поздрави
 

0
17/10/2021 00:21:02
Smeshan avatar Smeshan 89 Точки

Благодаря за насоките!

Ето каква я свърших:..
Създадох още файлове:
..
> sdl_utils
    > config
        > tools
            > ConfigApplier.h
            > ConfigExtractor.h
            > ConfigValidator.h
      > MonitorConfig.h
      > MonitorConfig.txt

 

MonitorConfig.txt

WINDOW_NAME:Hardwear renderering
DISPLAY_HEIGHT:800
DISPLAY_WIDTH:600

Сложих всички enum-и константи в MonitorConfig.h. Раздробих на класове. И вече EngineConfigLoader.cpp изглежда така:

. . .
/* C++ system icnludes */
#include <string>
#include <unordered_map>

/* Third-party icnludes */

/* Own icnludes */
#include "sdl_utils/config/tools/ConfigExtractor.h"
#include "sdl_utils/config/tools/ConfigApplier.h"

const char *MONITOR_CFG_FILE = "../sdl_utils/config/MonitorConfig.txt";

typedef std::unordered_map<std::string, std::string> configData;

static void populateMonitorConfig(MonitorConfig &outConfig) {
    const configData data = ConfigExtractor::readFromFile(MONITOR_CFG_FILE);
    ConfigApplier::setConfigs(data, outConfig);
}
. . .


По-добре е, ще видим като добавяме настройки дали ще сработва.
Мисля, че мястото на тези файлове не са в sdl_utils и май да изкарам папка tools отвън? Защото могат да се ползват и за други данни.

Поздрави!

П.С. Сега като го гледам втори път, не ми харесва в ConfigApplier.h да има windowName и т .н., неща които нямат общо с config applier. Може би не трябваше да го изкарвам от EngineConfigLoader.cpp, тоест да върна съдържанието на setConfigs() обратно както си беше. :?

1
17/10/2021 14:08:10
Smeshan avatar Smeshan 89 Точки

Здравейте, аз продължавам да надграждам, като сега исках автоматично да се зарежда информацията за ресурсите. Тоест само да трябва да сложа картинка в папката и толкова.

Направих го, като единственото, което се изисква е когато се добавя нов файл, да се прибави името към enum-a TextureId в CommonDefines.h и самия файл да следва този pattern на именуване:
 [ NAME ][ _ ][ ID ][ _ ][ WIDTH ][ _ ][ HEIGHT ]

Ползвам dirent.h иначе пробвах с filesystem.h но по някакви прични не поработи (поне при Linux).
Успях и да преизползвам ConfigValidator.h от предишното ми включване :)

Ето файловете:
AutoResLoader.h
AutoResLoader.cpp

Като в EngineConfigLoader.cpp нещата изглеждат така:

static void populateImageContainerConfig(ImageContainerConfig& cfg) {
    cfg.imageData = AutoResLoader::getFileConfigFromFolder();
}

Съответно ImageContainerConfig държи вектор. Направил съм го така, защото искам някак си да стигна до момента, в който да ползвам enum-ите от CommonDefines при init() в Game.cpp и да зареждам всичко с един for цикъл. Тоест да не правя променливи за всеки обект, а да имам вектор + enum и така ще мога лесно да се ориентирам и да им задавам параметрите. Примерно imgs[PLAYER].pos= .. и т.н.
Опитах се, но нещо го омазах нещо и се отказах за сега. :Х А и gameConfig остана празен.

Та в крайна сметка populate-a на картинките в EngineConfigLoader.cpp изглежда така:

static void populateImageContainerConfig(ImageContainerConfig& cfg) {
    cfg.imageData = AutoResLoader::getFileConfigFromFolder();

    /* for (const auto& image :  cfg.imageData) {
        std::cerr << "Current file info -> ID:" << image.id << " Name: "
            << image.name << " Location: " << image.location << " Width: "
            << image.width << " Height: " << image.height << std::endl;
    } */
}

* оставил съм една помощна фунцкия в коментар да се наблюдава кои файлове се зареждат

Поздрави,
Илиян

П.С. Мислех да го кача в git и да го споделя така, но прецених, че така или иначе исках да мина през отделнтие фази и да обясня какво съм направил, а и не са много новите моменти

1
22/10/2021 17:10:14
Valleri avatar Valleri 304 Точки

Лично аз, не намирам много забава да си пиша къстъм конфиг формат.
За моето приложение, най-вероятно, ще ползвам нещо като JSON и ще ползвам библиотека за десериализация на този формат.

1
Smeshan avatar Smeshan 89 Точки

Привет,

ще споделиш ли после как си го направил?
Аз нямам никаква представа за какво говориш, ще ми интересно да видя как става, а ако имаш време и да кажеш на два на три, ще съм много благодарен.

Поздрави!

0
Valleri avatar Valleri 304 Точки

Здрасти,
понеже не искам да съм голословен, седнах и го направих.
Само да отбележа, че Ц++ знанията ми са на ниво фундаменталс, но мисля че мога да ти споделя подхода си, а ти да прецениш и да направиш собствен прочит на решението.
 

Всички файлове са в тази папка. config.json е самата конфигурация, другите файлове надявам се съм наименовал интуитивно.
И да споделя къде съм заредил конфига. Ето тук.

П.С. Като цяло кода ми е ужасен и не ми харесва как съм инициализирал конфига най-отгоре в цпп файла, но засега не знам по-добър начин.

1
Smeshan avatar Smeshan 89 Точки

Здравейте, пак съм аз.
Сега след като направих това да се зареждат настройките автоматично и стигнат да въвеждам Sprite анимациите, по-някакви причини не мога да го направя renderer-a да ми покаже само част от картинката.

Доколкото разбирам Renderer::drawImage, destRect трябва да е с цялата големина на картината? Нали?
И от него sourceRect да взема само тази част, която искаме.
Но пък в същото време width и height в drawParams ги посочваме (гледах урока поне 5 пъти) да са равни на първия фрейм.
И тук вече тотално се обърквам и не разбирам защо ако трябва да е така, е така.. Нали зареждаме картинката, не я слагаме в никакъв правоъгълник, после в Image казваме drawParams какви да са и все излиза смачкана..
Явно има нещо общо с автоматичното зареждане на настройки, но те изплюват един вектор с настройки (за картинката с момиченцето са Width: 256 Height: 220 Frames: 6):

static void populateImageContainerConfig(ImageContainerConfig& cfg) {
    AutoResLoader resLoader;
    resLoader.init();

    const std::vector<Resource> imgFiles = resLoader.getResources(IMAGES);
    const std::vector<ImageConfig> imgConfigs = ConfigProcessor::processImageFiles(imgFiles);

    for (const auto& config : imgConfigs) {
        cfg.imageConfigs.insert(std::make_pair(config.id, config));
        //emplace
    }

и от там всичко трябва да е еднакво както в урока? Единствено в ImageConfig аз си запазвам width и height, но пробвах и да ги махна и да работя само с първия фрейм - същия резултат, така че предполагам грешката е преди това.

И имах съвсем друга идея да правя последните 5-6 часа и изобщо не мръднах. 3-4 пъти го преправях, сменях пробвах.. грр

https://github.com/Smeshan/SFX

Ако някой има време да погледне, ще се радвам и ще съм много благодарен!

Поздрави,
Илиян

0
Smeshan avatar Smeshan 89 Точки

Оправих се..

Не бях сменил на всички места където пише SDL_RenderCopy да взема сорс правоъгълник и го бях написал само тук:
if (FULL_OPACITY != drawParams.opacity) {
..
(facepalm)

1
Можем ли да използваме бисквитки?
Ние използваме бисквитки и подобни технологии, за да предоставим нашите услуги. Можете да се съгласите с всички или част от тях.
Назад
Функционални
Използваме бисквитки и подобни технологии, за да предоставим нашите услуги. Използваме „сесийни“ бисквитки, за да Ви идентифицираме временно. Те се пазят само по време на активната употреба на услугите ни. След излизане от приложението, затваряне на браузъра или мобилното устройство, данните се трият. Използваме бисквитки, за да предоставим опцията „Запомни Ме“, която Ви позволява да използвате нашите услуги без да предоставяте потребителско име и парола. Допълнително е възможно да използваме бисквитки за да съхраняваме различни малки настройки, като избор на езика, позиции на менюта и персонализирано съдържание. Използваме бисквитки и за измерване на маркетинговите ни усилия.
Рекламни
Използваме бисквитки, за да измерваме маркетинг ефективността ни, броене на посещения, както и за проследяването дали дадено електронно писмо е било отворено.