авто, шаблон?Как построить метод, возвращающий разные типы данных - PullRequest
0 голосов
/ 09 июня 2018

[ИНФО]

Я новичок здесь и ... новичок в программировании.Я изучаю C (с очень небольшим количеством C ++) около года, и теперь я застрял.В настоящее время я пишу свое первое более крупное приложение для учебных целей и, как и любой программист, стараюсь быть как можно более ленивым.Мое приложение основано на Simple Fast Multimedia Library (SFML) для рисования графики и, конечно, C / C ++ для логики.Я устал от множества различных переменных, скрытых в коде (например, разрешение окна, положение окна и т. Д.), Поэтому я начал писать класс для чтения * txt файлов с конфигурацией (вы знаете: config.ini с чем-то вроде «iWindowResolutionX = 1920;»).Алгоритм, который открывает * txt файлы, интерпретирует их и извлекает необходимые данные, работает (вероятно, это очень плохо - но эй, это работает: P)

[ВОПРОС] Я застрял с очень простой вещью.У меня есть объект моего класса чтения конфигурационных файлов, и я хочу добиться чего-то вроде этого:

Int main()
{
int WindowResolutionX = 0; //nothing special - int with value of 0.
CfgReader cfg_object("config.ini"); // object of CfgReader class with custom 
                                  constructor passing name of *txt 
                                  configuration file.
WindowResolutionX = cfg_object.Search("iWindowResolutionX"); // As you see - I want to write a code that calls method "Search" on cfg_object. This method takes variable name as parameter and after sucsess (value found) returns it.

И вот я застрял.Как заставить метод возвращать различные базовые типы данных (char, int, float и т. Д.)?}

Я пытался определить тип возвращаемого значения метода как "auto", но он выдает ошибку "функция, которая возвращает auto, не может быть использована до того, как она будет определена" (я не понимаю, что такое VSговорит со мной), а затем выдает ошибки при попытке вернуть типы данных, отличные от первоначально выбранных (это просто, если компилятор впервые видит «возвращаемое значение», он не может позволить мне вернуть любой другой тип данных).

Следующее, что я попробовал, это методы шаблонов, но я глупо это понимать;)

Хорошо, есть простое решение - я могу скопировать свой метод поиска X раз для типов данных Iхочу и просто перегрузить его - но это не изящное решение и не научит меня чему-то новому.Ниже мой код:

"CfgReader.h"

#pragma once
#include "fstream"
#include <iostream>
#include <string>


class CfgReader
{
public:
FILE *fp;
const char * cfg_filename;
size_t filesize;
std::string line;
int int_val;
float float_val;
char char_val;
bool bool_val;
long long_val;
std::string string_val;
size_t size_t_val;


public:
void OpenFile(const char * filename);
void CloseFile();
auto Search(const char * search_val);
void Show_content();

int r_int();
char r_char();
float r_float();
size_t r_size_t();
long r_long();
bool r_bool();
std::string r_string();

CfgReader();
CfgReader(const char *);
~CfgReader();
};  

"CfgReader.cpp"

#include "CfgReader.h"
#pragma warning(disable: 4996)

CfgReader::CfgReader()
{
    CfgReader("");
}

CfgReader::CfgReader(const char * filename)
{
    if ((sizeof(filename) == 1) && (filename[0] == 0))
    {
        std::cout << "\n CfgReader No filename.";
        cfg_filename = "";
        fp = NULL;
    }
    else
    {
    std::cout << "\n test";
    line = "";
    int_val = NULL;
    float_val = NULL;
    char_val = NULL;
    bool_val = false;
    long_val = NULL;
    string_val = "";
    size_t_val = NULL;
    cfg_filename = filename;
    OpenFile(cfg_filename);
    }
}

void CfgReader::OpenFile(const char * filename)
{
    fp = fopen(filename, "rb");
    if (fp != NULL)
    {
        std::cout << "\n good!";
    }
    else
    {
        std::cout << "\n Error, could not open file.";
    }
    rewind(fp);
    fseek(fp, 0, SEEK_END);
    filesize = ftell(fp);
    rewind(fp);
    std::cout << "\n filesize: " << filesize;
    //system("pause");
}



void CfgReader::Search(const char * search_val)
{

    size_t search_val_length = 0;
     for (search_val_length; search_val[search_val_length] != '\0'; 
search_val_length++);
std::string test;
if (fp == NULL)
{
    std::cout << "\n Error, file not loaded!";
}
else
{
    char first_letter = 0;
    bool match = false;
    size_t counter = 0;
    rewind(fp);
    while (match == false)
    {
        while (first_letter != 13)
        {
            fread(&first_letter, sizeof(char), 1, fp);
        }
        if (first_letter == 13)
        {
            fread(&first_letter, sizeof(char), 1, fp);
            if (first_letter == 10)
            {
                do
                {
                    fread(&first_letter, sizeof(char), 1, fp);
                    if (first_letter == search_val[counter])
                    {
                        test += first_letter;
                        counter++;
                        if(counter==search_val_length)
                        {
                            match = true;
                            break;
                        }

                    }
                    else
                    {
                        counter = 0;
                        test = "";
                        break;
                    }
                } while (first_letter != 61);
                if (test == search_val || match == true)
                {
                    match = true;
                    break;
                }
            }
        }
        std::cout << "\n ftell(fp): " << ftell(fp);
        if (ftell(fp) == filesize) break;
    }
    if (match == false)
    {
        std::cout << "\n ERROR, no such VALUE!";
    }
    else
    {
        std::cout << "\n test string = " << test;
        //system("pause");
        //Show_content();
        ///line = test;
        ///test = "";
        while (first_letter != 13)
        {
            fread(&first_letter, sizeof(char), 1, fp);
            if (first_letter == 61)continue;
            test += first_letter;
        }
        std::cout << "\n test string  VALUE (string):" << test << std::endl;
        switch (line[0])
        {
        case 'i':
            int_val = std::stoi(test);
            std::cout << "\n int_val: " << int_val;
            //return int_val;
            //a = int_val;
            break;
        case 'f':
            float_val = std::stof(test);
            std::cout << "\n float_val: " << float_val;
            //return float_val;
            //a = float_val;
            break;
        case 'b':
            if (test[0] == 'f' || test[0] == '0')   bool_val = false;
            else bool_val = true;
            std::cout << "\n bool_val: " << bool_val;
            //return bool_val;
            //a = bool_val;
            break;
        case 'l':
            long_val = std::stol(test);
            std::cout << "\n long_val: " << long_val;
            //return long_val;
            //a = long_val;
            break;
        case 's':
            string_val = test;
            std::cout << "\n string_val: " << string_val;
            //return string_val;
        //  a = string_val;
            break;
        case 't':
            size_t_val = std::stoul(test);
            std::cout << "\n size_t_val: " << size_t_val;
            //return size_t_val;
            //a = size_t_val;
            break;

        }

    }
}
}

int CfgReader::r_int()
{
    return int_val;
}
char CfgReader::r_char()
{
    return char_val;
}
float CfgReader::r_float()
{
    return float_val;
}
size_t CfgReader::r_size_t()
{
    return size_t_val;
}
long CfgReader::r_long()
{
    return long_val;
}
bool CfgReader::r_bool()
{
    return bool_val;
}
std::string CfgReader::r_string()
{
    return string_val;
}

void CfgReader::Show_content()
{
    std::cout << "\n //--------------------------CfgReader.Show_content()------------------------\\"<<std::endl;
    if (fp != NULL)
    {
    rewind(fp);
    fseek(fp, 0, SEEK_END);
    int filesize = ftell(fp);
    char literka;
    rewind(fp);
    while (ftell(fp) != filesize)
    {
        fread(&literka, sizeof(char), 1, fp);
        std::cout << "\n" << (short)literka << " - " << literka;
    }
}
else
{
    std::cout << "\n Error: fp == NULL.";
}
std::cout << "\n \\--------------------------/CfgReader.Show_content()------------------------// \n";
}

void CfgReader::CloseFile()
{
    fclose(fp);
}

CfgReader::~CfgReader()
{
} 

"Source.cpp"

#pragma warning(disable:4996)
#include "CfgReader.h"
int main()
{

CfgReader Config("config.ini");
Config.Search("iBitDepth");

system("pause");
return 0;
}

"config.ini"

//------------------------ M_UPTIME config FILE ------------------------//
[Window]
iWindowResX=1920
iWindowResY=1080
fWindowScale=1
iWindowPosX=-1920
iWindowPosY=0
iBitDepth=32

[Screen Saver]
iScreenSaverTimeLimit=600

[TestValues]
bThisIsBool=false
bThisIsBoolToo=true
sThisIsString=Ala ma kota a kot ma ale

Любой может указать мне, как преобразовать метод поиска для возможности возвращать разные типы данных:

 int main()
{
int value_1 = 0;
CfgReader Config("config.ini");
value_1 = Config.Search("iBitDepth");
return 0;    
}

или работать здесь: (Объект CfgReader получает ссылку на переменную в качестве параметра)

int main()
{
int value_1 = 0;
CfgReader Config("config.ini");
Config.Search("iBitDepth", &value_1);
return 0;    
}

Если бы кто-то мог также дать мне пример того, как правильно преобразовать мой метод поиска для метода шаблона.Спасибо за ответы, у меня нет идей ...

Ответы [ 2 ]

0 голосов
/ 09 июня 2018

Я бы предложил реализовать отдельные методы для каждого типа данных.Вы знаете тип данных целевой переменной, верно?Тогда вы можете сделать несколько таких методов:

int GetInt(const char* name, int defaultValue = 0) const;
std::string GetString(const char* name, const char* defaultValue = "") const;

И вызвать соответствующий метод:

int bitDepth = config.GetInt("Window.iBitDepth", 24);
0 голосов
/ 09 июня 2018

Один из способов сделать это с помощью шаблонов - использовать «лексическое приведение» (как называет это Boost).Реализация простой лексической функции приведения с использованием std::istringstream относительно проста:

template<typename TargetType>
TargetType lexical_cast(const std::string& source)
{
    TargetType result;
    // create a stream for reading, initially containing source string
    std::istringstream stream(source);
    // read one value of type TargetType...
    stream >> result;
    // ...and return it
    return result;
}

Это будет работать для всех типов, для которых operator >> перегружена, включая примитивные типы, такие как float или int.Кроме того, вы можете перегрузить оператор для своих пользовательских типов.

С этим вы можете реализовать Search шаблонный метод, который преобразует строку в запрошенный тип:

template<typename TargetType>
TargetType Search(const std::string& key) const
{
    std::string valueAsStr = // get it somehow, from a map, or file...
    return lexical_cast<TargetType>(valueAsStr);
}

Я пытался определить тип возвращаемого значения метода как «auto», но он выдает ошибку «функция, которая возвращает« auto », не может быть использована до того, как она будет определена» (я не понимаю, что VS говорит со мной здесь)

Лично я не использую VS слишком часто, но я предполагаю, что это означает, что компилятор должен видеть тело (определение) метода при его вызове.Это необходимо - в конце концов, при использовании auto (без завершающего возвращаемого типа) компилятор должен выводить возвращаемый тип на основе тела функции.

...