2

I need to read csv file using already written library that returns column value always as string, so as part of validation and further processing i need to convert that string value to appropriate type (which can be double, int, enum, bool, date etc.) and here is what I had written but this is giving error that there are multiple overloads for stod/stoi etc. Also is there any better approach to accomplish this task.

bool convertFunction(T a, R& b,std::function<R (T)> fx)
{
    bool isConverted = true;
    try
    {
        b = fx(a);
    }
    catch(const std::exception& e)
    {
        isConverted = false;
    }
    return isConverted;
}
int main() {
    std::string x = "2.54";
    double y = 0.0;
    bool isValid = convertFunction(x,y,std::stod);
    std::cout<<"value of y is "<<y<<std::endl;
    return 0;
}
PapaDiHatti
  • 1,841
  • 19
  • 26
  • Why not use `stod` directly? – Ulrich Eckhardt May 24 '22 at 07:08
  • As i need to read multiple fields from csv and then convert them to appropriate type using different conversion functions like std::stod, std::stoi, static_cast etc. and this might create lot of similar type of code which might be difficult to maintain – PapaDiHatti May 24 '22 at 07:15
  • @JeJo yes i know the type before calling the function – PapaDiHatti May 24 '22 at 07:21
  • 3
    `std::stod` is an overloaded set of functions and the compiler has no idea which one you mean. Besides, taking an address of a standard library function (with a few exceptions) is UB. You can wrap `stod` with your own function and pass that. Tangentially, `std::function` might not be the best idea, you may want to consider another template parameter instead. – n. m. could be an AI May 24 '22 at 07:37
  • can you elaborate why std::function should not be used and what could be the alternative? – PapaDiHatti May 24 '22 at 08:02
  • 1
    @PapaDiHatti How would you want to implement a type that is capable of holding arbitrary callable types such as lambdas, functors, ordinary function pointers, ... – guess, that gets a rather heavy beast. *Could* be a polymorphic template approach, could be something else. In your case a simple function pointer likely would suffice as well: `R(*fx)(T)` – Aconcagua May 24 '22 at 08:20

2 Answers2

2

A totally generic approach might look as follows:

template <typename T>
bool convert(std::string const& text, T& value)
{
    std::istringstream s(text);
    s >> value;
    char c;
    return s && (s >> c, s.eof());
}

Reading yet another character is expected to fail with end-of-file flag to be set, this assures that the entire string has been read – then failing if trailing whitespace is available, though, so you might yet want to make the function tolerant against.

Aconcagua
  • 24,880
  • 4
  • 34
  • 59
  • std::string x = "abc";double y = 0.0;bool isValid = convert(x,y); Getting this error: "error: no match for ‘operator>>’ (operand types are ‘std::istringstream’ {aka ‘std::__cxx11::basic_istringstream’} and ‘char’) return s && s >> c, s.eof();" – PapaDiHatti May 24 '22 at 07:28
  • @PapaDiHatti Parentheses lacking – sorry... Sequence (comma) operator has lower precedence than &&... – Aconcagua May 24 '22 at 07:31
  • @PapaDiHatti Well, strange – lacking parentheses should have led to potentially wrong result, but not to compilation fail... https://godbolt.org/ – Aconcagua May 24 '22 at 07:34
  • sorry my bad forgot to include sstream header file, checking now – PapaDiHatti May 24 '22 at 07:36
  • Thanks @Aconcagua this is working for int, double, char etc. but not for enums enum days{unknown,monday,tuesday,wednesday};int x = 1;days y = unknown;bool isValid = convert(x,y); – PapaDiHatti May 24 '22 at 07:44
  • 1
    Then you need to provide a custom `operator>>` for your enums – C++ doesn't provide a native string representation for these, though it would be quite useful. With an X-macro you could generate both enum and operator at the same time... A similar approach arose from another question, you might be [interested in](https://stackoverflow.com/a/72125616/1312382), though you definitly would apply some changes (generating the `operator>>` instead of two enum variants). – Aconcagua May 24 '22 at 08:23
1

If you really want to go the template route...

The fix for your implementation is to wrap std::stod inside a lambda that takes a definitive set of parameters. Then assign that lambda to a std::function that matches what the template expects. I also updated the code to pass items by const reference a bit more consistently.

#include <string>
#include <functional>
#include <iostream>

template <typename T, typename R>
static bool convertFunction(const T& a, R& b, std::function<R (const T&)>& fx)
{
    bool isConverted = true;
    try
    {
        b = fx(a);
    }
    catch(const std::exception& e)
    {
        isConverted = false;
    }
    return isConverted;
}

int main() {
    std::string x = "2.54";
    double y = 0.0;

    std::function<double (const std::string&)> S2D = [](const std::string& s) -> double {
        return std::stod(s);
    };

    convertFunction(x, y, S2D);

    std::cout<<"value of y is "<<y<<std::endl;
    return 0;
}
selbie
  • 100,020
  • 15
  • 103
  • 173
  • Thanks @selbie this is working :) but one question though Is this trailing return type mandatory ? – PapaDiHatti May 24 '22 at 07:59
  • @PapaDiHatti - I don't think you need to explicitly declare the `-> double` thing. – selbie May 24 '22 at 08:02
  • 1
    @selbie No, definitively not. Same rules for return type apply as for functions with an `auto` return type only... – Aconcagua May 24 '22 at 08:16
  • Btw if i use lambda function directly instead of S2D then i am getting template argument deduction/substitution failed:‘main()::’ is not derived from ‘std::function’}); – PapaDiHatti May 24 '22 at 08:57
  • @PapaDiHatti - You can inline a lambda if you pass it by value instead of by reference. Change the declaration of convertFunction to be `bool convertFunction(const T& a, R& b, std::function fx)` - That is, drop the `&` before the `fx` in the function declaration. That seems to work. I'd need a language lawyer to explain that to me. – selbie May 24 '22 at 16:16