c++ delay printf until needed - c++

In c++, Is it possible to write to some kind of buffer with printf (or similar) and then later in the program either write the buffer to the screen or discard it depending on the outcome.
I want to do this because I have a recursive function and only want the see the things printed throughout the recursion if the result is of interest.

The class std::ostringstream is what you are looking for.
In C++, formatted IO is done (preferably) through the <iostream> library. This is the famous cout << variable << endl.
cout outputs directly to the standard output. If you want to buffer instead, you can redirect your output to a std::ostringstream instance that you can later redirect to the standard out:
#include <iostream>
[...]
ostringstream buf;
buf << myVar1 << "MyStr" << endl;
[...] // some time later
cout << buf.str();
If you prefer the printf way of doing things, you can use sprintf (though I won't recommend it). It's a bit more complex because you need to know the size of the buffer in advance.
char myBuf[10000]; // up to you do to the proper bound checking
sprintf(myBuf, "format %d", myvar);
[...] // you may want to use strcat and such for more complex operations
printf(myBuf);

Certainly. You can leverage the power of vsnprintf for that purpose. I'd suggest some sort of class wrapping an std::string or std::vector<char> (essentially the same in C++11):
#include <cstdargs>
#include <cstdio>
#include <string>
class Formatter
{
std::string buf;
public:
void add(char const * fmt, ...)
{
std::va_list ap, aq;
va_start(ap, fmt);
va_copy(aq, ap);
int ret1 = std::vsnprintf(NULL, 0, fmt, ap);
// check ret1 != -1
std::size_t cur = buf.size();
buf.resize(cur + ret1 + 1);
int ret2 = std::vsnprintf(&buf[cur], ret1 + 1, fmt, aq);
// check ret2 != -1
buf.resize(cur + ret1);
va_end(aq);
va_end(ap);
}
std::string const & str() const { return buf; }
};
Now you can say:
Formatter f;
f.add("Hello, %s", "world");
f.add("%i%i%i", 1, 2, 3);
std::cout << f.str() << std::endl;
If you're very concerned about performance, you can try and preallocate some space for the print operation and maintain a separate "end" position, in the hope that you'll never have to run the vnsprintf call more than once.

What is about using a string ?
Or a string array. Or a collection ?
Gathering all data u need and printing if needed ?

You could use sprintf function which does the same thing as printf into char buffer. But you shall not. These old C-style functions are obsolete in C++, you shall use streams instead. Looks like std::stringstream fit you needs.

For a recursing function, the best way would be to delay getting the result, not printing it, so instead of this:
int fact( int n )
{
printf("%d", n);
if( n!=1 )
return n * fact(n - 1);
else return 1;
};
<....>
fact( 5 );
you might use this:
int fact( int n )
{
if( n!=1 )
return n * fact(n - 1);
else return 1;
};
<....>
int result = fact( 5 );
printf("%d", result);
Basically, print it only when it's ready. If for some reasons you can't do it directly, save the result into some kind of a buffer variable, and access it after the function ends.

Related

Converting std::vector<char> to char* causes defective characters

I have a function in my code called buildPacket that takes some parameters, and converts them into a char* and adds them together using a std::vector<char> and at the end returns the result as a char*. The problem is that after I convert the vector to a char* all characters become a weird character.
I tried using other ways of converting the vector to a char*, like with using reinterpret_cast<char*>. When I print the contents of the vector from inside the function, I get the expected result so the problem is with the conversion.
The function's code:
char* buildPacket (int code, std::string data)
{
char* codeBytes = CAST_TO_BYTES(code);
std::vector<char> packetBytes(codeBytes, codeBytes + sizeof(char));
size_t dataLength = data.size() + 1;
char* dataLengthBytes = CAST_TO_BYTES(dataLength);
packetBytes.insert(packetBytes.end(), dataLengthBytes, dataLengthBytes + sizeof(int));
const char* dataBytes = data.c_str();
packetBytes.insert(packetBytes.end(), dataBytes, dataBytes + dataLength);
return &packetBytes[0];
}
The CAST_TO_BYTES macro:
#define CAST_TO_BYTES(OBJ) static_cast<char*>(static_cast<void*>(&OBJ));
The intent of the function is to take the input and build a packet out of it to send through a socket later on, the packet's format consists of a 1-byte long code, 4-byte long data length and data with variable length.
The input I gave it is code = 101 and data = "{\"password\":\"123456\",\"username\":\"test\"}"
This is the result I am getting when printing the characters: ▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌
EDIT: Thanks for all the help, I've returned a vector<char> at the end as suggested and took a different approach in converting to values to a char*.
You're returning a pointer to something inside of a local variable. You should change your code to have your vector<char> alive outside of your buildPacket function (such as by returning it instead of the char*).
You might try this solution. I thing using STL makes it more clearer what you are trying to achieve. There was also an undefined reference in your code, that could lead to unpredictable crashes.
#include <iostream>
#include <string>
#include <vector>
#include <iterator>
// Better return std::vector<char>
char* buildPacket(int code, const std::string& data)
{
auto result = data;
result.append(1, static_cast<char>(code));
char* ret = new char[data.size() + 2];
ret[data.size() + 1] = '\0';
std::copy(result.begin(), result.end(), ret);
return ret;
}
std::vector<char> buildPacketStl(int code, const std::string& data)
{
std::vector<char> ret;
std::copy(data.begin(), data.end(), std::back_inserter(ret));
ret.push_back(static_cast<char>(code));
return ret;
}
int main() {
std::cout << buildPacket(65, "test") << std::endl;; // 65 -> A
auto stl= buildPacketStl(65, "test"); // 65 -> A
std::copy(stl.begin(), stl.end(), std::ostream_iterator<char>(std::cout, ""));
std::cout << std::endl;
}

How to get characters out of stringstream without copy?

What is the proper c++11 way to extract a set of characters out of a stringstream without using boost?
I want to do it without copying, if possible, because where this is used is in a critical data loop. It seems, though, std::string does not allow direct access to the data.
For example, the code below performs a substring copy out of a stringstream:
inline std::string left(std::stringstream ss, uint32_t count) {
char* buffer = new char[count];
ss.get(buffer, count);
std::string str(buffer); // Second copy performed here
delete buffer;
return str;
}
Should I even be using char *buffer according to c++11?
How do I get around making a second copy?
My understanding is that vectors initialize every character, so I want to avoid that.
Also, this needs to be passed into a function which accepts const char *, so now after this runs I am forced to do a .c_str(). Does this also make a copy?
It would be nice to be able to pass back a const char *, but that seems to go against the "proper" c++11 style.
To understand what I am trying to do, here is "effectively" what I want to use it for:
fprintf( stderr, "Data: [%s]...", left(ststream, 255) );
But the c++11 forces:
fprintf( stderr, "Data: [%s]...", left(str_data, 255).c_str() );
How many copies of that string am I making here?
How can I reduce it to only a single copy out of the stringstream?
You could use something like described in this link: How to create a std::string directly from a char* array without copying?
Basically, create a string, call the resize() method on the string with the size that is passed to your function and then pass the pointer to the first character of the string to the stringstring.get() method. You will end up with only one copy.
inline std::string left(std::stringstream& ss, uint32_t count) {
std::string str;
str.resize(count);
ss.get(&str[0], count);
return str;
}
My suggestion:
Create the std::string to be returned by giving it the size.
Read the characters one by one from the stringstream and set the values in the std::string.
Here's what the function looks like:
inline std::string left(std::stringstream ss, uint32_t count) {
std::string str(count+1, '\0');
for (uint32_t i = 0; i < count; ++i )
{
int c = ss.getc();
if ( c != EOF )
{
str[i] = c;
}
else
{
break;
}
}
return str;
}
R Sahu, this I like! Obvious now that I see it done. ;-)
I do have one mod though (as well as passed a shared_ptr of stream which is what I actually had in my version):
In your initializer, you are filling with nulls. You only need to fill with the last one, so I propose a tweak of this:
inline std::string left(std::shared_ptr<std::stringstream> ss, uint32_t count) {
std::string str;
str.reserve(count + 1);
uint32_t i;
for(i = 0; i < count; ++i) {
int c = ss->get();
if(c != EOF) {
str[i] = c;
} else {
break;
}
}
str[i] = '\0';
return str;
}
Now, only initialized with nulls on a single character.
Thanks R Sahu!
If the purpose of this function is solely for passing to fprintf or another C-style stream, then you could avoid allocation completely by doing the following:
void left(FILE *out, std::stringstream &in, size_t count)
{
in.seekg(0);
char ch;
while ( count-- && in.get(ch) )
fputc(out, static_cast<unsigned char>(ch));
}
Usage:
fprintf( stderr, "Data: [" );
left(stderr, stream, 255);
fprintf( stderr, "] ...\n");
Bear in mind that another seekg will be required if you try to use the stream reading functions on the stringstream later; and it would not surprise me if this is the same speed or slower than the options involving str().

pushing multiple variable values in buffer

void char() {
char buff[50];
int l;
int i = 0;
i = sprintf(buff,a,b,c);
for(i=0,i<=l,l++)
serial.println(buff);
}
I want to have a buffer that will be printed like #123#234#
a=123
b=234
is it the correct way to do that? I tried but I didn't get any output. Probably silly mistake.
EDIT:
#include <stdio.h>
void abc() {
int a = 123;
int b = 234;
char buff[50];
int l;
int i = 0;
l = sprintf(buff,"#%d#%d#",a,b);
for(i=0;i<=l;l++);
printf("%s", buff);
//return buff;
//serial.println(buff);
}
int main() {
//char bhg = abc();
//printf("&d", bhg);
abc();
}
I tried with this.Working properly but taking time to give output. Can I make it fast? Probably it is trying to iterate through that char[50] for allocation.
That's not how you use sprintf. You need to use a format string. Like:
l = sprintf(buff,"#%d#%d#",a,b);
Also, you are assigning the returned length to i in your code, not l.
I'm sure I'm probably violating SO protocol here, but base on your comments, you can do:
a = 100;
b = 23;
Serial.print('#');
Serial.print(a);
Serial.print('#');
Serial.println(b);
This will send this string of characters to your serial port: "#100#23", and in what is probably the most efficient way possible.
Forget about sprintf (which is almost impossible to use safely). Something like:
std::ostringstream buff;
buff << "#" << a << "#" << b << "#";
serial.println( buff.str().c_str() );
should be all you need. (Although I'm not sure: what is l in your code, and why do you loop over it?)

How to use C++ std::ostream with printf-like formatting?

I am learning C++. cout is an instance of std::ostream class.
How can I print a formatted string with it?
I can still use printf, but I want to learn a proper C++ method which can take advantage of all C++ benefits. I think this should be possible with std::ostream, but I can't find the proper way.
In C++20 you can use std::format for safe printf-like formatting:
std::cout << std::format("The answer is {}.\n", 42);
In addition to that the {fmt} library, std::format is based on, provides the print function that combines formatting and output:
fmt::print("The answer is {}.\n", 42);
Disclaimer: I'm the author of {fmt} and C++20 std::format.
The only thing you can do with std::ostream directly is the well known <<-syntax:
int i = 0;
std::cout << "this is a number: " << i;
And there are various IO manipulators that can be used to influence the formatting, number of digits, etc. of integers, floating point numbers etc.
However, that is not the same as the formatted strings of printf. C++11 does not include any facility that allows you to use string formatting in the same way as it is used with printf (except printf itself, which you can of course use in C++ if you want).
In terms of libraries that provide printf-style functionality, there is boost::format, which enables code such as this (copied from the synopsis):
std::cout << boost::format("writing %1%, x=%2% : %3%-th try") % "toto" % 40.23 % 50;
Also note that there is a proposal for inclusion of printf-style formatting in a future version of the Standard. If this gets accepted, syntax such as the below may become available:
std::cout << std::putf("this is a number: %d\n",i);
This is an idiom I have gotten used to. Hopefully it helps:
// Hacky but idiomatic printf style syntax with c++ <<
#include <cstdlib> // for sprintf
char buf[1024]; sprintf(buf, "%d score and %d years ago", 4, 7);
cout << string(buf) <<endl;
&
I suggest using ostringstream instead of ostream
see following example :
#include <vector>
#include <string>
#include <iostream>
#include "CppUnitTest.h"
#define _CRT_NO_VA_START_VALIDATION
std::string format(const std::string& format, ...)
{
va_list args;
va_start(args, format);
size_t len = std::vsnprintf(NULL, 0, format.c_str(), args);
va_end(args);
std::vector<char> vec(len + 1);
va_start(args, format);
std::vsnprintf(&vec[0], len + 1, format.c_str(), args);
va_end(args);
return &vec[0];
}
example usage:
std::ostringstream ss;
ss << format("%s => %d", "Version", Version) << std::endl;
Logger::WriteMessage(ss.str().c_str()); // write to unit test output
std::cout << ss.str() << std::endl; // write to standard output
To implement printf one could use c++11 template parameters:
#include <iostream>
#include <string>
inline std::ostream & mprintf(std::ostream & ostr, const char * fstr) throw()
{
return ostr << fstr;
}
template<typename T, typename... Args>
std::ostream & mprintf(std::ostream & ostr,
const char * fstr, const T & x) throw()
{
size_t i=0;
char c = fstr[0];
while (c != '%')
{
if(c == 0) return ostr; // string is finished
ostr << c;
c = fstr[++i];
};
c = fstr[++i];
ostr << x;
if(c==0) return ostr; //
// print the rest of the stirng
ostr << &fstr[++i];
return ostr;
}
template<typename T, typename... Args>
std::ostream & mprintf(std::ostream & ostr,
const char * fstr, const T & x, Args... args) throw()
{
size_t i=0;
char c = fstr[0];
while (c != '%')
{
if(c == 0) return ostr; // string is finished
ostr << c;
c = fstr[++i];
};
c = fstr[++i];
ostr << x;
if(c==0) return ostr; // string is finished
return mprintf(ostr, &fstr[++i], args...);
}
int main()
{
int c = 50*6;
double a = 34./67.;
std::string q = "Hello!";
// put only two arguments
// the symbol after % does not matter at all
mprintf(std::cout, "%f + %f = %a \n", c, a);
// print string object: for real printf one should write q.c_str()
mprintf(std::cout, "message: \"%s\". \n", q);
// the last argument will be ignored
mprintf(std::cout, "%z + %f\n", (long)a, 12, 544 );
}
Output
300 + 2 = %a
message: "Hello!".
2 + 12
This a very simple code and it can be improved.
1) The advantage is that it uses << to print objects to the stream, so you can put arbitrary arguments that can be output via <<.
2) It ignores the type of the argument in the formatted string: after % can stand arbitrary symbol even a space. The output stream decides how to print the corresponding object. It also compatible with printf.
3) A disadvantage is that it can not print the percent symbol '%', one need to slightly improve the code.
4) It can not print formatted numbers, like %4.5f
5) If the number of arguments is less than predicted by formatted string, then the function just print the rest of the string.
6) If the number of arguments is greater than predicted by formatted string, then the remained arguments are ignored
One can improve the code to make 2)-6) to fully mimic the printf behaviour.
However, if you follow the rules of printf, then only 3) and 4) need essentially to be fixed.
I wrote independently but came up with answer similar to user3283405
My solution uses vasprintf() to acheive formatting, and uses operator overloading of << of std::ostream to free the memory in right place.
Usage:
std::cout << putf(const char *format, ...); //Same format as C printf(3)
Code:
#define _GNU_SOURCE
#include <cstdarg>
#include <iostream>
#include <cstdio>
struct putf_r{
char *s;
};
putf_r putf(const char *fmt, ...){
va_list ap;
va_start(ap, fmt);
putf_r a;
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wformat-nonliteral"
vasprintf(&a.s, fmt, ap);
#pragma GCC diagnostic pop
va_end(ap);
return a;
}
std::ostream& operator<<(std::ostream& os, putf_r a){
os<<a.s;
free(a.s);
return os;
}
int main(){
std::cout << putf("%3d\n", 23) << putf("%a\n", 256.);
}
Note that compiler doesn't check format inside putf(), so compiler flag -Wformat-nonliteral will not warn for suspicious code in putf() and you need to care uncontrolled format string problem by yourself.
Detailed info can be found on GitHub
Field Width
Setting field width is very simple. For each variable, simply precede it with "setw(n)". Like this:
#include <iostream>
#include <iomanip>
using namespace std;
int main()
{
const int max = 12;
const int width = 6;
for(int row = 1; row <= max; row++) {
for(int col = 1; col <= max; col++) {
cout << setw(width) << row * col;
}
cout << endl;
}
return 0;
}
Notice how "setw(n)" controls the field width, so each number is
printed inside a field that stays the same width regardless of the
width of the number itself.
-- From "Programming/C++ tutorial" by P. Lutus.
Sample output:
2017-12-20T16:24:47,604144+01:00 Hello, World!
Code (with put_printf usage demonstrated in put_timestamp):
#include <assert.h>
#include <chrono>
#include <iomanip>
#include <iostream>
class put_printf {
static constexpr size_t failed = std::numeric_limits<size_t>::max(); // for any explicit error handling
size_t stream_size; // excluding '\0'; on error set to 0 or to "failed"
char buf_stack[2048+1]; // MAY be any size that fits on the stack (even 0), SHOULD be (just) large enough for most uses (including '\0')
std::unique_ptr<char[]> buf_heap; // only used if the output doesn't fit in buf_stack
public:
explicit put_printf(const char *format, ...)
#if __GNUC__
__attribute__ ((format (printf, 2, 3))) // most compelling reason for not using a variadic template; parameter 1 is implied "this"
#endif
{
va_list args;
va_start(args, format);
const int res = vsnprintf(buf_stack, sizeof(buf_stack), format, args);
va_end(args);
if (res < 0) { // easily provoked, e.g., with "%02147483646i\n", i.e., more than INT_MAX-1 significant characters (only observed, no guarantee seen)
stream_size = failed;
} else if (res < sizeof(buf_stack)) { // preferred path
stream_size = res;
} else { // not artificially constrained
try {
const size_t buf_size = static_cast<size_t>(res) + 1; // avoids relying on "res < INT_MAX" (only observed, no guarantee seen)
buf_heap.reset(new char[buf_size]); // observed to work even beyond INT_MAX=2^32-1 bytes
va_start(args, format);
if (vsnprintf(buf_heap.get(), buf_size, format, args) == res) stream_size = res;
else stream_size = failed; // can't happen
va_end(args);
} catch (const std::bad_alloc&) { // insufficient free heap space (or an environment-specific constraint?)
stream_size = failed;
}
}
}
friend std::ostream& operator<<(std::ostream& os, const put_printf& self) {
if (self.stream_size == failed) {
// (placeholder for any explicit error handling)
return os;
} else {
// using write() rather than operator<<() to avoid a separate scan for '\0' or unintentional truncation at any internal '\0' character
return os.write((self.buf_heap ? self.buf_heap.get() : self.buf_stack), self.stream_size);
}
}
};
class put_timestamp {
const bool basic = false;
const bool local = true;
public:
friend std::ostream& operator<<(std::ostream& os, const put_timestamp& self) {
const auto now = std::chrono::system_clock::now();
const std::time_t now_time_t = std::chrono::system_clock::to_time_t(now);
struct tm tm; if ((self.local ? localtime_r(&now_time_t, &tm) : gmtime_r(&now_time_t, &tm)) == nullptr) return os; // TODO: explicit error handling?
static_assert(4 <= sizeof(int), "");
const int microseconds = std::chrono::duration_cast<std::chrono::microseconds>(now.time_since_epoch() % std::chrono::seconds(1)).count();
assert(0 <= microseconds && microseconds < 1000000); // TODO: (how) do we know?
// TODO: doesn't "point" in "decimal_point()" imply "dot"/"full stop"/"period", unlike an obviously neutral term like "mark"/"separator"/"sign"?
const char decimal_sign = std::use_facet<std::numpunct<char>>(os.getloc()).decimal_point() == '.' ? '.' : ','; // full stop accepted, comma preferred
// TODO: all well and good for a locale-specific decimal sign, but couldn't the locale also upset microseconds formatting by grouping digits?
os << std::put_time(&tm, self.basic ? "%Y%m%dT%H%M%S" : "%FT%T") << put_printf("%c%06i", decimal_sign, microseconds);
if (! self.local) return os << "Z";
const int tz_minutes = std::abs(static_cast<int>(tm.tm_gmtoff)) / 60;
return os << put_printf(self.basic ? "%c%02i%02i" : "%c%02i:%02i", 0 <= tm.tm_gmtoff ? '+' : '-', tz_minutes / 60, tz_minutes % 60);
}
};
int main() {
// testing decimal sign
///std::cout.imbue(std::locale("en_GB"));
///std::cout.imbue(std::locale("fr_FR"));
std::cout << put_timestamp() << " Hello, World!\n";
#if 0
typedef put_printf pf; // just to demo local abbreviation
std::cout << "1: " << pf("%02147483646i\n" , 1 ) << std::endl; // res < 0
std::cout << "2: " << pf("%02147483643i%i\n", 1, 100) << std::endl; // res < 0
std::cout << "3: " << pf("%02147483643i%i\n", 1, 10) << std::endl; // works
std::cout << "4: " << pf("%02147483646i" , 1 ) << std::endl; // works
#endif
return 0;
}
Comments about put_printf:
// Reasons for the name "put_printf" (and not "putf" after all):
// - put_printf is self-documenting, while using the naming pattern also seen in std::put_time;
// - it is not clear whether the proposed std::putf would support exactly the same format syntax;
// - it has a niche purpose, so a longer name is not an objection, and for frequent local uses
// it is easy enough to declare an even shorter "typedef put_printf pf;" or so.
// Evaluation of delegating to vsnprintf() with intermediate buffer:
// (+) identical result without implementation and/or maintenance issues,
// (?) succeeds or fails as a whole, no output of successful prefix before point of failure
// (-) (total output size limited to INT_MAX-1)
// (-) overhead (TODO: optimal buf_stack size considering cache and VM page locality?)
// Error handling (an STL design problem?):
// - std::cout.setstate(std::ios_base::failbit) discards further std::cout output (stdout still works),
// so, to be aware of an error in business logic yet keep on trucking in diagnostics,
// should there be separate classes, or a possibility to plug in an error handler, or what?
// - should the basic or default error handling print a diagnostic message? throw an exception?
// TODO: could a function "int ostream_printf(std::ostream& os, const char *format, ...)"
// first try to write directly into os.rdbuf() before using buf_stack and buf_heap,
// and would that significantly improve performance or not?
When I need both the typesafety of cout and the quick and easy formatting of simple variables of printf(), I mix the two like this. This is an ugly fix, but it gets things done for me when I need to output things like "02/07/2014 10:05am" together with some more complex entities:
#include <stdio>
#include <stdarg>
#include <stdlib>
#include <iostream>
#pragma hdrstop
using namespace std;
char* print(char* fmt, ...)
{
static char buffer[80] = "";
va_list argptr;
va_start(argptr,fmt);
vsprintf(buffer, fmt, argptr);
va_end(argptr);
return buffer;
}
#pragma argsused
int main(int argc, char* argv[])
{
cout << print("\n%06d\n%6d\n%6d\n%010.3f",1,12,123,123.456);
system("PAUSE>NUL");
return 0;
}

Convert non-null-terminated char* to int

I am working on some code that reads in a data file. The file frequently contains numeric values of various lengths encoded in ASCII that I need to convert to integers. The problem is that they are not null-terminated, which of course causes problems with atoi. The solution I have been using is to manually append a null to the character sequence, and then convert it.
This is the code that I have been using; it works fine, but it seems very kludgy.
char *append_null(const char *chars, const int size)
{
char *tmp = new char[size + 2];
memcpy(tmp, chars, size);
tmp[size + 1] = '\0';
return tmp;
}
int atoi2(const char *chars, const int size)
{
char *tmp = append_null(chars, size);
int result = atoi(tmp);
delete[] tmp;
return result;
}
int main()
{
char *test = new char[20];
test[0] = '1';
test[1] = '2';
test[2] = '3';
test[3] = '4';
cout << atoi2(test, 4) << endl;
}
I am wondering if there is a better way to approach this problem.
Fixed-format integer conversion is still well within handroll range where the library won't do:
size_t mem_tozd_rjzf(const char *buf, size_t len) // digits only
{
int n=0;
while (len--)
n = n*10 + *buf++ - '0';
return n;
}
long mem_told(const char *buf, size_t len) // spaces, sign, digits
{
long n=0, sign=1;
while ( len && isspace(*buf) )
--len, ++buf;
if ( len ) switch(*buf) {
case '-': sign=-1; \
case '+': --len, ++buf;
}
while ( len-- && isdigit(*buf) )
n = n*10 + *buf++ -'0';
return n*sign;
}
In C++11, you can say std::stoi(std::string(chars, size)), all from <string>.
int i = atoi(std::string(chars, size).c_str());
Your method will work, although you should only need size+1 for appending the null and the null will go at position size. Currently, your test code doesn't actually make the function call, but I'll assume that you have a way to determine when the null-terminated characters end. If possibly, I'd recommend making the null termination there so that you don't have to worry about catching cases where you hit an exception before you can deallocate the memory (memory which, honestly, may or may not have been allocated if you start catching exceptions).
std::string str = "1234";
boost::lexical_cast<int>(str); // 1234
The problem as formulated requires to construct a string given an array of known size, then converting its text into a numeric value.
To convert text into values, C++ has a unified mechanism: streams.
In your case, you can do the following:
int i = 0;
std::stringstream(std::string(yourbuffer, yoursize)) >> i;
This will completely avoid any plain old C reference.
But, since -as you say- all values come from a file... why just don't read the file itself as a stream via std::fstream ?
The question says (emph mine):
The file frequently contains numeric values of various lengths encoded
in ASCII that I need to convert to integers. The problem is that they
are not null-terminated, which of course causes problems with atoi.
This does not really pose a problem, as, if we look at the docs for atoi or strtol, they clearly state:
Function discards any whitespace characters until first non-whitespace
character is found. Then it takes as many characters as possible to
form a valid integer number representation and converts them to
integer value.
That means, it doesn't matter at all that the numbers aren't null terminated, as long as they are delimited by something that stops conversion.
And if they are not delimited, then you have to know the size, and when you know the size, I would also recommend a hand-coded solution like in the other answer.
I know this answer is not answering OP's question, but it helps if your source of char* is a char array with known size.
Live demo
#include <fmt/core.h>
#include <type_traits>
#include <iostream>
// SFINAE fallback
template<typename T, typename =
std::enable_if< std::is_pointer<T>::value >
>
int charArrayToInt(const T arr){ // Fall back for user friendly compiler errors
static_assert(false == std::is_pointer<T>::value, "`charArrayToInt()` dosen't allow conversion from pointer!");
return -1;
}
// Valid for both null or non-null-terminated char array
template<size_t sz>
int charArrayToInt(const char(&arr)[sz]){
// It doesn't matter whether it's null terminated or not
std::string str(arr, sz);
return std::stof(str);
}
int main() {
char number[2] = {'4','2'};
int ret = charArrayToInt(number);
fmt::print("The answer is {}. ", ret);
return 0;
}