首页 > 解决方案 > auto, template? How to construct method returning different datatypes

问题描述

[INFO]

I'm new here and... new to programming. I'm learning C (with very little of C++) for about a year and now I'm stuck. I'm currently writing my first bigger application for training purposes and as every programist - I'm trying to be as lazy as I could. My application is based on Simple Fast Multimedia Library (SFML) for drawing graphics and of course C/C++ for logic. I was tired of having lots of different variables hidden in code (like Window Resolution, Window Position etc.) soo I started writing class for reading *txt files with configuration (You know: config.ini with something like "iWindowResolutionX=1920;"). Algorythm that opens *txt files, interprets them and pulls out needed data is working (proppably it is very bad - but hey, it is working :P)

[QUESTION] I'm stuck with very basic thing. I have object of my config file reader class and I want to achieve something like this:

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.

And here I am stuck. How to force method to return different basic data types( char, int, float etc. )? }

I've tried defining method return type as "auto", but it gives me an error "a function that returns 'auto' cannot be used before it is defined" (I don't understand what's VS is talking to me here) and then errors about trying to return different datatypes than firstly selected (this is simple, if compiler see "return value" for the first time, it can't allow me to return any other datatype).

Next thing I've tried was template methods, but I'm to stupid to understand it ;)

Ok, there is simple solution - I can copy my Search method X times for X datatypes I want and just simply overload it - but this is not elegant solution and won't teach me anything new. Below my code:

"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

Anyone can point me out how to convert Search method for being able to either return different datatypes:

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

or work as here: (CfgReader object gets reference to variable as a parameter)

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

If someone could also give me a example on how to properly convert my Search method for template method. Thanks for responces, I'm out of ideas...

标签: c++templatesauto

解决方案


使用模板执行此操作的一种方法是使用“词法转换”(正如 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 >>重载的类型,包括原始类型,例如floator 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(没有尾随返回类型)时,编译器必须根据函数的主体推断返回类型。


推荐阅读