Професионална програма
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 370 Точки

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

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

Главният проблем, е че във 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 301 Точки

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

1
Smeshan avatar Smeshan 89 Точки

Привет,

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

Поздрави!

0
Valleri avatar Valleri 301 Точки

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

Всички файлове са в тази папка. 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