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;
}
6
  • Why not use stod directly? Commented May 24, 2022 at 7: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 Commented May 24, 2022 at 7:15
  • @JeJo yes i know the type before calling the function Commented May 24, 2022 at 7: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. Commented May 24, 2022 at 7:37
  • 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) Commented May 24, 2022 at 8:20

2 Answers 2

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.

Sign up to request clarification or add additional context in comments.

6 Comments

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<char>’} and ‘char’) return s && s >> c, s.eof();"
@PapaDiHatti Parentheses lacking – sorry... Sequence (comma) operator has lower precedence than &&...
@PapaDiHatti Well, strange – lacking parentheses should have led to potentially wrong result, but not to compilation fail... godbolt.org
sorry my bad forgot to include sstream header file, checking now
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, though you definitly would apply some changes (generating the operator>> instead of two enum variants).
|
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;
}

5 Comments

Thanks @selbie this is working :) but one question though Is this trailing return type mandatory ?
@PapaDiHatti - I don't think you need to explicitly declare the -> double thing.
@selbie No, definitively not. Same rules for return type apply as for functions with an auto return type only...
Btw if i use lambda function directly instead of S2D then i am getting template argument deduction/substitution failed:‘main()::<lambda(const string&)>’ is not derived from ‘std::function<R(const T&)>’});
@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<R (const T&)> 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.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.