std::stoi is throwing some errors in specific cases. I don't want to use try/catch block, so I googled a little bit about char convertion and saw that std::from_chars was doing exactly what I wanted, without those try/catch block.
Obviously, using std::from_chars directly works pretty well, but I don't like the syntax. So I started writing my own ToInt(), ToFloat(), etc. And was like : "ok, that sounds stupid, let's use template".
Here I am :
#include <string>
#include <charconv>
#include <iostream>
template <typename T>
T ToNumber(const char* str, T varType)
{
if (!str)
return 0;
T var = 0;
std::from_chars(str, str + strlen(str), var);
return var;
}
int main()
{
std::string t = "123.5";
auto a = ToNumber(t.c_str(), (int)0); // a will be an int (123)
float b = ToNumber(t.c_str(), (int)0); // b will store the value as int (123)
auto c = ToNumber(t.c_str(), (float)0); // c will be a float (123.5)
std::cout << a;
return 0;
}
It actually works, that is not the problem. But I was wondering... Is it a good practice to send a "false argument" (T varType) in order to get the cast done and enable auto ?
If not, what is the clever way to write such a function ?
Thanks in advance
You are not checking the return value of std::from_chars for failure, eg:
auto [p, ec] = std::from_chars(str, str + strlen(str), var);
if (ec != std::errc()) {
...
}
That said, another way to deal with the template argument T without using a type-casted input parameter is to simply specify the desired type explicitly at the call site instead, eg:
template <typename T>
T ToNumber(const char* str)
{
if (!str)
return T{};
T var{};
auto [p, ec] = std::from_chars(str, str + strlen(str), var);
if (ec != std::errc())
return T{};
return var;
}
auto a = ToNumber<int>(t.c_str());
float b = ToNumber<int>(t.c_str());
auto c = ToNumber<float>(t.c_str());
Otherwise, you can use template argument deduction via a reference output parameter (just like std::from_chars() does), eg:
template <typename T>
bool ToNumber(const char* str, T &var)
{
if (!str)
return false;
auto [p, ec] = std::from_chars(str, str + strlen(str), var);
return (ec == std::errc());
}
int a;
ToNumber(t.c_str(), a);
int tmp;
ToNumber(t.c_str(), tmp);
float b = tmp;
float c;
ToNumber(t.c_str(), c);
Related
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;
}
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.
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;
}
I'm trying to write a function that would return a substring from the given start and end indices. This is the code that I've written but when i run it it gives me error. I'm using gtest to run it not main().
template <typename S>
S substring(S string_, int Istart, int Iend)
{
S substr = string_[Istart];
for(int i=(Istart+1); i<Iend; i++)
{
substr += string_[i];
}
return substr;
}
And this is the error i get:
In file included from test.cpp:2:0:
lab2.cpp: In instantiation of ‘S substring(S, int, int) [with S = const char*]’:
test.cpp:56:1: required from here
lab2.cpp:91:23: error: invalid conversion from ‘char’ to ‘const char*’ [-fpermissive]
S substr = string_[Istart];
~~~~~~~^
My test code is this:
TEST(subStr, T1){
string str="he";
EXPECT_EQ(str,substring("hello", 0, 2));
}
TEST(subStr, T2){
string str="urge";
EXPECT_EQ(str,substring("hamburger", 4, 8));
}
With the given calling context, this:
EXPECT_EQ(str,substring("hello", 0, 2));
utilizes an expansion of
substring<const char*>
and therefore the resulting code becomes:
const char* substring(const char* string_, int Istart, int Iend)
{
const char* substr = string_[Istart];
for(int i=(Istart+1); i<Iend; i++)
{
substr += string_[i];
}
return substr;
}
Clearly that's not going to work. const char* substr = string_[Istart]; is initializing const char * from char. As I see it you have two choices, but only one of them remotely realistic. Since EXPECT_EQ tests equivalence, going the pointer route will not work no matter what. You need to have a comparable std::string guaranteed on at least one side of that test, and the only way to guarantee that whilst still affording your expressive arguments is like this:
template<class S>
std::string substring(S s, int Istart, int Iend)
{
return std::string(s).substr(Istart, Iend - Istart);
}
This will take anything compatible to std::string as a source argument. The result is ALWAYS a std::string, which can then be used in comparison against a variety of things, including char[N], const char *, and of course, std::string.
It still has a huge caveat, that being Istart and Iend must be ordered. Eliminating that frailty is the very reason the standard library substr member of std::string doesn't take a begin,end; it takes a begin,length. Nonetheless, this is easily the simplest way to do what you want.(the single-line nature calling out the very usefulness of such a thing notwithstanding).
Example
I don't have GoogleTest, but a simple assert macro will demonstrate testing against different comparisons to std::string will work:
#include <iostream>
#include <string>
#include <cassert>
template<class S>
std::string substring(S s, int Istart, int Iend)
{
return std::string(s).substr(Istart, Iend - Istart);
}
int main()
{
const char *kvalptr = "welcome";
std::string kvalstr = kvalptr;
auto res = substring("abdwelcomedef", 3, 10);
// test that both lhs prospects test against our result
assert(kvalptr == res);
assert(kvalstr == res);
// output res and one of the prospects.
std::cout << res << '\n' << kvalptr << '\n';
}
Output
welcome
welcome
Assuming that the intended type is string. A += operator exists for the string type.
A simple fix is to replace
S substr = string_[Istart];
with
S substr;
substr += string_[Istart];
which will not work for const char* types as pointed out
The following works for both string and const char*.
#include <exception>
#include <iostream>
#include <functional>
#include <string>
#include <type_traits>
template <typename S>
S substring(S string_, int Istart, int Iend)
{
if constexpr (std::is_same_v<S, std::string>) {
if(Istart < Iend){
return string_.substr(Istart, Iend-Istart);
}
}
else if constexpr (std::is_same_v<S, const char*>)
{
if(Istart < Iend){
std::string temp = std::string(string_);
return (temp.substr(Istart, Iend-Istart)).c_str();
}
}
else throw;
}
I'm using C++17 with templates and recursion to replace the C Va_Args.
Currently only floats are supported, more types are following once float is working ;)
class CWrite
{
public:
template<typename NextT, typename ...RestT>
static std::string Format(NextT next, RestT ... rest);
private:
template<typename T>
static constexpr bool is_float = std::is_same_v<T, float>;
template<typename T>
static constexpr bool IsValidParam();
template<typename LastT>
static std::string Format(LastT last);
///Empty param case
static std::string Format();
};
// +++++++++++++++++++ Implementation ++++++++++++++++++++++++++
template<typename T>
constexpr bool CWrite::IsValidParam()
{
bool bRes = false;
bRes |= is_float<T>;
return bRes;
}
template<typename NextT, typename ...RestT>
std::string CWrite::Format(NextT next, RestT ... rest)
{
std::string strRes = Format(next);
strRes += Format(rest...);
return strRes;
}
template<typename LastT>
std::string CWrite::Format(LastT last)
{
std::string strRes;
if (is_float<LastT>)
{
strRes = "float:";
char buffer[10] = { };
snprintf(buffer, 10, "%f", last);
strRes += buffer;
}
return strRes;
}
///Empty param case
std::string CWrite::Format()
{
return "";
}
calling this with
std::string strRes = CWrite::Format(1.0f, 2.0f, 3.0f, 4.0f, 5);
results in a warning for snprintf()
format '%f' expects argument of type 'double', but argument 4 has type 'int'
I'd expect that IsValidParam returns false for the last argument which should be an int.
https://onlinegdb.com/B1A72GHgU
Could you help me out here?
Did i miss something here?
If you can use C++17, you should use if constexpr in the following function
template<typename LastT>
std::string CWrite::Format(LastT last)
{
std::string strRes;
// VVVVVVVVV <-- add "constexpr" here
if constexpr (is_float<LastT>)
{
strRes = "float:";
char buffer[10] = { };
snprintf(buffer, 10, "%f", last);
strRes += buffer;
}
return strRes;
}
The problem is that, using a simple if instead if constexpr, the compiler has to compile the statement (the part inside the { ... }) also when is_float<LastT> is false.
If you can't use C++17... I suppose you can differentiate the function through overloading
std::string CWrite::Format (float last)
{
std::string strRes { "float:" };
char buffer[10] = { };
snprintf(buffer, 10, "%f", last);
return strRes += buffer;
}
std::string CWrite::Format (int last)
{
std::string strRes { "int:" };
char buffer[10] = { };
snprintf(buffer, 10, "%i", last);
return strRes += buffer;
}
max66's answer addresses the reason why your method has a problem with the format string and how to fix it. Basically you just need some way of picking a different format string based on the type of the value being formatted.
However, I'd like to point out another flaw: you assume that any given value will only require 9 characters to convert into a string. For very large values (e.g. 1e22) this will fail. GCC will actually issue you a warning if it can determine this at compile time.
Additionally, your current implementation allocates many strings and recursively appends them together. This is - of course - highly inefficient and diminishes the speed of the printf family of functions to the point that it's not really worth using them.
Also your solution doesn't check for format errors (snprintf() returns negative in this case). And in such cases you may be appended undefined memory onto your string, as I'm not certain the C standard guarantees to null terminate the buffer on failure cases (but it might).
My solution is to have a function that formats a given argument in-place onto the end of a std::string. Additionally it handles format errors and cases where 9 bytes is insufficient to hold the formatted value.
Additionally I impose SFINAE restrictions on the argument types to ensure it can only be called with types that we support.
Here's my solution with comments to explain what does what and why:
#include <string>
#include <type_traits>
#include <iostream>
// checks if T is a type we support
template<typename T>
inline constexpr bool allowed_type = std::is_floating_point_v<T> || std::is_integral_v<T>;
// the initial amount of space for stringifying each argument
constexpr std::size_t APPEND_PADDING = 20;
// returns the appropriate format string for type T (T assumed to be supported)
template<typename T>
const char *fmt_string()
{
if constexpr (std::is_floating_point_v<T>) return "%f";
else return "%d";
}
// stringifys val onto the end of str (T assumed to be supported)
template<typename T>
void append(std::string &str, T val)
{
std::size_t prev_size = str.size(); // remember the previous size of str
str.resize(prev_size + APPEND_PADDING); // allocate the space we need
const char *fmt = fmt_string<T>(); // get the format string to use
// format the value and check the save the return value
int res = snprintf(&str[prev_size], APPEND_PADDING, fmt, val);
// on format error, just skip it (or )
if (res < 0) str.resize(prev_size);
// if we didn't have enough room we need to try again with the correct size
if ((std::size_t)res >= APPEND_PADDING)
{
str.resize(prev_size + res + 1); // make space for the characters we need and the null terminator
snprintf(&str[prev_size], res + 1, fmt, val); // format the string again (this time will work)
str.pop_back(); // remove the null terminator
}
// otherwise we had enough room, so just truncate to the written characters
else str.resize(prev_size + res);
}
// formats all of args into a single string (only allows supported types)
template<typename ...Args, std::enable_if_t<(allowed_type<Args> && ...), int> = 0>
std::string format(Args ...args)
{
std::string str; // create an empty buffer string to store the result
str.reserve(sizeof...(args) * APPEND_PADDING); // predict how much space we'll need for everything
int _[] = { (append(str, args), 0)... }; // append all the args to str one at a time
(void)_; // suppress unused variable warnings (will just be optimized away)
return str;
}
int main()
{
std::cout << format(1, 2, 2.3, 3, 4.4, 5, 1e22) << '\n';
}
Note that this runs all the formatted strings together with no separation. Fixing this would be as simple as changing the format strings returned from fmt_string().
I used different function names than you did, but you get the idea. format() is the function you would use.
I have a list of functions that need to be applied to a single string additively. How do I express the "Apply" function.
auto outPutString = inputString
.Apply(Transformation1)
.Apply(Transformation2)
in c++?
The string is the std::string
From C++ 11 onwards, you may also write an Apply function using variadic templates:
template <typename OutputT, typename InputT>
OutputT Apply(const InputT &obj)
{
return obj;
}
template <typename OutputT, typename InputT, typename Func, typename... OtherFuncs>
OutputT Apply(const InputT &obj, Func f, OtherFuncs... other)
{
return Apply<OutputT, decltype(f(obj))>(f(obj), other...);
}
Then you may use this as follows:
auto res = Apply<std::string>(
"Hello",
[](const std::string &str) { return str + " "; }, // Applicator 1
[](const std::string &str) { return str + "World"; } // Applicator 2
);
The result in this case is »Hello World«.
Because the above construction distinguishes between InputT and OutputT, you may "mix" types, as in:
auto res = Apply<size_t>(
"Hello",
[](const std::string &str) { return str + " World"; }, // Applicator 1
[](const std::string &str) { return str.size(); } // Applicator 2
);
This time the result is 11.
Finally, if you really want to use chaining syntax, you could write a class that wraps the initial object and has an Apply method.
Like this:
auto outPutString = Transformation2(Transformation1(inputString));
std::string manipulateString(std::string str) {/* do something */; return result;}
std::string manipulateStringAgain(std::string str) {/* do something else */; return result;}
std::string manipulateMe = "hello";
auto resultString = manipulateString(manipulateStringAgain(manipulateMe));
I'm going to assume when you say "a list of functions", you mean one that varies at runtime. Other answers are better if the list is static.
#include <vector>
#include <string>
#include <functional>
#include <numeric>
std::vector<std::function<std::string(std::string)>> funcs = { Transformation1, Transformation2 }; // or gotten from wherever
auto output = std::accumulate(funcs.begin(), funcs.end(), input, [](auto acc, auto fun){ return fun(acc); });
It is possible in C and C++ as well, to define pointer to a fuction and to create vector of pointers to functions. Later, you can invoke functions inside a loop with desired arguments. Please let me know if you are interested for details.
If you would like to keep the order, create some wrapping class and put your manipulation functions in there. For example:
#include <iostream>
#include <string>
using namespace std;
class StringManipulator
{
public:
StringManipulator(std::string str) : str(str) {}
operator std::string() {return str;}
StringManipulator& doSomething() {str += "1"; return *this;}
StringManipulator& doSomethingElse() {str += "2"; return *this;}
private:
std::string str;
};
int main() {
std::string result = StringManipulator("0").doSomething().doSomethingElse();
std::cout << result;
return 0;
}
Output is 012.
operator std::string ensures implicit conversion.
#include <vector>
#include <iostream>
// three funcitons with a string as the parameter
int funca(std::string& str)
{
std::cout << "funca:" << str << std::endl;
return 1;
}
int funcb(std::string& str)
{
std::cout << "funcb:" << str << std::endl;
return 2;
}
int funcd(std::string& str)
{
std::cout << "funcd:" << str << std::endl;
return 3;
}
int main()
{
// definition of the string
std::string str = "the string";
// declare vector of pointers to function returning an int and receiving a string as a parameter:
std::vector< int(*)(std::string&)> pf;
// load func pointers to vector:
pf.push_back(&funca);
pf.push_back(&funcb);
pf.push_back(&funcd);
//declare vector iterator:
std::vector<int (*)(std::string&)>::iterator it;
// iterate vector of func pointers:
for (it = pf.begin() ; it != pf.end(); ++it)
{
// function call using pointers and passing parameter str
// you can get return value as from 'normal' function
int ret = (*it)(str);
std::cout << "function returns:" << ret << std::endl;
}
}
/*
compiled and executed on ubuntu 18.04, output:
funca:the string
function returns:1
funcb:the string
function returns:2
funcd:the string
function returns:3
*/
I am rewriting a C wrapper around a C Python API (Python 1.5) and I noticed that the function Py_VaBuildValue uses variadic number of args. I wondered if I have to use the same in my C++ function wrapper to pass to this function or if there is a more C++ way to deal with this?
I know variadic functions can be the cause of untold trouble, so I'd rather avoid having to use if there is a better way.
EDIT:
So here is the C code I need to make into a C++ function:
int Set_Global(char *modname, char *varname, char *valfmt, ... /* cval(s) */) {
int result;
PyObject *module, *val; // "modname.varname = val"
va_list cvals;
va_start(cvals, valfmt); // C args after valfmt
module = Load_Module(modname); // get/load module
if (module == NULL)
return -1;
val = Py_VaBuildValue(valfmt, cvals); // convert input to Python
va_end(cvals);
if (val == NULL)
return -1;
result = PyObject_SetAttrString(module, varname, val);
Py_DECREF(val); // set global module var
return result; // decref val: var owns it
}
So I'm making the same function with std::string instead of char* and I want to change the ellipsis to something more c++ like, that I can however then pass to Py_VaBuildValue inside the function.
If you want to be clever and don't fear some heavy template wizardry, it should be possible to generate (or massage) valfmt to always match the types you want to pass (I am assuming it uses format specifiers similar to printf, but the technique is applicable to any kind of format specification). You could do something like:
template <typename T> struct format_trait;
template <> struct format_trait<int> { static const char * format() { return "%i"; }};
template <> struct format_trait<unsigned> { static const char * format() { return "%u"; }};
... // and so on for each type you want to use
template <typename Arg1>
int Set_Global(const std::string &modname, const std::string &varname, const Arg1 &arg1)
{
return ::Set_Global(modname.c_str(), varname.c_str(), format_trait<Arg1>::format(), arg1);
}
template <typename Arg1, typename Arg2>
int Set_Global(const std::string &modname, const std::string &varname, const Arg1 &arg1, const Arg2 &arg2)
{
return ::Set_Global(modname.c_str(), varname.c_str(),
std::string(format_trait<Arg1>::format()) + format_trait<Arg2>::format(),
arg1, arg2);
}
... // Repeat up to number of argument you reasonably expect to need or use C++0x variadic templates.
This is the simple way where each value is formatted the default way and combined together. If you want something more complex, you can create a function, that will get valfmt string and correct format specifiers (obtained from the trait) and will fix up the format string to match.
You may write template function which will check the correctness of params, and won't allow your program to crash.
bool BuildValueCheck(const char * s, int v)
{
if( s[0] == 'i' )
return true;
return false;
}
bool BuildValueCheck(const char * s, float v)
{
if( s[0] == 'f' )
return true;
return false;
}
bool BuildValueCheck(const char * s, char * v)
{
if( s[0] == 's' || s[0] == 'z' )
return true;
return false;
}
// and so on for each other type
template<typename t1>
PyObject *BuildValue(char * format, t1 v1)
{
char * s = strchr(format, "ifsz...."); // Skip here all "()[]" etc
if( !s )
return NULL; // and print an error
if(!BuildValueCheck(s, v1))
return NULL; // and also print an error
return Py_BuildValue(format, v1);
}
template<typename t1, typename t2>
PyObject *BuildValue(char * format, t1 v1, t2 v2)
{
// Skip here all "()[]" etc
char * s = strchr(format, "ifsz....");
if( !s )
return NULL;
if(!BuildValueCheck(s, v1))
return NULL;
s = strchr(s+1, "ifsz....");
if( !s )
return NULL;
if(!BuildValueCheck(s, v2))
return NULL;
return Py_BuildValue(format, v1, v2);
}
// and so on for 3,4,5 params - I doubt your program uses more
// and then replace all Py_BuildValue with BuildValue across the code, or make a #define