Black magic when using fopen!?

F

fdm

In a function I call fopen:



template<class V, class Dt>
unsigned int LoadFile(std::string & path) {
std::cout << "path = " << path << std::endl;
FILE *fptr = fopen(path.c_str(), "rb");
if (fptr==NULL) {
std::cout << "file not found!" << path << std::endl;
}
...
...
}



When I run it I get this:

path = /home/fdm/test.h2
file not found!

fptr is NULL, meaning that it cannot find the file. I have double checked
that the file exist in this location.

Now here comes the wierd part. If I manually set the SAME path in the
function like:



template<class V, class Dt>
unsigned int LoadFile(std::string & path) {
std::cout << "before = " << path << std::endl;
path = "/home/fdm/test.h2";
std::cout << "after = " << path << std::endl;
FILE *fptr = fopen(path.c_str(), "rb");
if (fptr==NULL) {
std::cout << "file not found!" << path << std::endl;
}
...
...
}



I get this:

before = /home/fdm/test.h2
after = /home/fdm/test.h2

and fptr is not NULL (the file is read successfully). But as can be seen
from the printed messages there is no difference between 'path' before and
after updating it! So why does it only work when I update 'path' manually in
the function??

I get this error on Ubuntu 9.04. If I run it on windows vista 64 bit in
visual studio it works fine! Any ideas??
 
A

AnonMail2005

In a function I call fopen:

    template<class V, class Dt>
    unsigned int LoadFile(std::string & path) {
      std::cout << "path = " << path << std::endl;
      FILE *fptr = fopen(path.c_str(), "rb");
      if (fptr==NULL) {
        std::cout << "file not found!" << path << std::endl;
      }
        ...
        ...
    }

When I run it I get this:

     path = /home/fdm/test.h2
     file not found!

fptr is NULL, meaning that it cannot find the file. I have double checked
that the file exist in this location.

Now here comes the wierd part. If I manually set the SAME path in the
function like:

    template<class V, class Dt>
    unsigned int LoadFile(std::string & path) {
         std::cout << "before = " << path << std::endl;
         path = "/home/fdm/test.h2";
         std::cout << "after = " << path << std::endl;
         FILE *fptr = fopen(path.c_str(), "rb");
         if (fptr==NULL) {
           std::cout << "file not found!" << path << std::endl;
         }
         ...
         ...
     }

I get this:

   before = /home/fdm/test.h2
   after = /home/fdm/test.h2

and fptr is not NULL (the file is read successfully). But as can be seen
from the printed messages there is no difference between 'path' before and
after updating it! So why does it only work when I update 'path' manually in
the function??

I get this error on Ubuntu 9.04. If I run it on windows vista 64 bit in
visual studio it works fine! Any ideas??

Perhaps there are trailing unprintable characters in the path string
passed to your function? Try printing the size of your string.

HTH
 
F

fdm

In a function I call fopen:

template<class V, class Dt>
unsigned int LoadFile(std::string & path) {
std::cout << "path = " << path << std::endl;
FILE *fptr = fopen(path.c_str(), "rb");
if (fptr==NULL) {
std::cout << "file not found!" << path << std::endl;
}
...
...
}

When I run it I get this:

path = /home/fdm/test.h2
file not found!

fptr is NULL, meaning that it cannot find the file. I have double checked
that the file exist in this location.

Now here comes the wierd part. If I manually set the SAME path in the
function like:

template<class V, class Dt>
unsigned int LoadFile(std::string & path) {
std::cout << "before = " << path << std::endl;
path = "/home/fdm/test.h2";
std::cout << "after = " << path << std::endl;
FILE *fptr = fopen(path.c_str(), "rb");
if (fptr==NULL) {
std::cout << "file not found!" << path << std::endl;
}
...
...
}

I get this:

before = /home/fdm/test.h2
after = /home/fdm/test.h2

and fptr is not NULL (the file is read successfully). But as can be seen
from the printed messages there is no difference between 'path' before and
after updating it! So why does it only work when I update 'path' manually
in
the function??

I get this error on Ubuntu 9.04. If I run it on windows vista 64 bit in
visual studio it works fine! Any ideas??

Perhaps there are trailing unprintable characters in the path string
passed to your function? Try printing the size of your string.




Good point and you are right:


Before:

pathB size = 78


After:

pathA size = 77

But how do I find out what the last char is? There is no trim function in
the stl but I have found:

void trim2(string& str)
{
string::size_type pos = str.find_last_not_of(' ');
if(pos != string::npos) {
str.erase(pos + 1);
pos = str.find_first_not_of(' ');
if(pos != string::npos) str.erase(0, pos);
}
else str.erase(str.begin(), str.end());
}

But even if I pass my string to this function it still has size 78
afterwards! Any ideas?
 
B

Balog Pal

fdm said:
In a function I call fopen:



template<class V, class Dt>
unsigned int LoadFile(std::string & path) {
std::cout << "path = " << path << std::endl;
FILE *fptr = fopen(path.c_str(), "rb");
if (fptr==NULL) {
std::cout << "file not found!" << path << std::endl;
}
...
...
}

When I run it I get this:

path = /home/fdm/test.h2
file not found! ....
I get this error on Ubuntu 9.04. If I run it on windows vista 64 bit in
visual studio it works fine! Any ideas??

I'd bet you have undefined behavior in your program elsewhere, so the
string's state is corrupt in the first place or the environment is. When you
call c_str() the content is gone... that is not supposed to happen.

Anyway, as first step you may try to output c_str() instead of the string,
or walk in debugger to inspect its state and what actually happens in
c_str() and what arrives to fopen().
 
R

red floyd

In a function I call fopen:
template<class V, class Dt>
unsigned int LoadFile(std::string & path) {
std::cout << "path = " << path << std::endl;
FILE *fptr = fopen(path.c_str(), "rb");
if (fptr==NULL) {
std::cout << "file not found!" << path << std::endl;
}
...
...
}
When I run it I get this:
path = /home/fdm/test.h2
file not found!
fptr is NULL, meaning that it cannot find the file. I have double checked
that the file exist in this location.
Now here comes the wierd part. If I manually set the SAME path in the
function like:
template<class V, class Dt>
unsigned int LoadFile(std::string & path) {
std::cout << "before = " << path << std::endl;
path = "/home/fdm/test.h2";
std::cout << "after = " << path << std::endl;
FILE *fptr = fopen(path.c_str(), "rb");
if (fptr==NULL) {
std::cout << "file not found!" << path << std::endl;
}
...
...
}
I get this:
before = /home/fdm/test.h2
after = /home/fdm/test.h2
and fptr is not NULL (the file is read successfully). But as can be seen
from the printed messages there is no difference between 'path' before and
after updating it! So why does it only work when I update 'path' manually
in
the function??
I get this error on Ubuntu 9.04. If I run it on windows vista 64 bit in
visual studio it works fine! Any ideas??

Perhaps there are trailing unprintable characters in the path string
passed to your function?  Try printing the size of your string.

Good point and you are right:

Before:

pathB size = 78

After:

pathA size = 77

But how do I find out what the last char is? There is no trim function in
the stl but I have found: [redacted]

But even if I pass my string to this function it still has size 78
afterwards! Any ideas?

Delimiters!!!!!!

std::cout << '"' << path << '"' << std::endl;
 
F

fdm

In a function I call fopen:
template<class V, class Dt>
unsigned int LoadFile(std::string & path) {
std::cout << "path = " << path << std::endl;
FILE *fptr = fopen(path.c_str(), "rb");
if (fptr==NULL) {
std::cout << "file not found!" << path << std::endl;
}
...
...
}
When I run it I get this:
path = /home/fdm/test.h2
file not found!
fptr is NULL, meaning that it cannot find the file. I have double
checked
that the file exist in this location.
Now here comes the wierd part. If I manually set the SAME path in the
function like:
template<class V, class Dt>
unsigned int LoadFile(std::string & path) {
std::cout << "before = " << path << std::endl;
path = "/home/fdm/test.h2";
std::cout << "after = " << path << std::endl;
FILE *fptr = fopen(path.c_str(), "rb");
if (fptr==NULL) {
std::cout << "file not found!" << path << std::endl;
}
...
...
}
I get this:
before = /home/fdm/test.h2
after = /home/fdm/test.h2
and fptr is not NULL (the file is read successfully). But as can be seen
from the printed messages there is no difference between 'path' before
and
after updating it! So why does it only work when I update 'path'
manually
in
the function??
I get this error on Ubuntu 9.04. If I run it on windows vista 64 bit in
visual studio it works fine! Any ideas??

Perhaps there are trailing unprintable characters in the path string
passed to your function? Try printing the size of your string.

Good point and you are right:

Before:

pathB size = 78

After:

pathA size = 77

But how do I find out what the last char is? There is no trim function in
the stl but I have found: [redacted]

But even if I pass my string to this function it still has size 78
afterwards! Any ideas?

Delimiters!!!!!!

std::cout << '"' << path << '"' << std::endl;




I don't understand is : -> " <- a delimiter? And I still need to make the
string identical to the manually typed string somehow.
 
F

Francesco S. Carta

"(e-mail address removed)" <[email protected]> wrote in message
Perhaps there are trailing unprintable characters in the path string
passed to your function? Try printing the size of your string.
Good point and you are right:

pathB size = 78

pathA size = 77
But how do I find out what the last char is? There is no trim function in
the stl but I have found: [redacted]

But even if I pass my string to this function it still has size 78
afterwards! Any ideas?

Delimiters!!!!!!

std::cout << '"' << path << '"' << std::endl;

I don't understand is : ->  "  <- a delimiter?

Yes, it is.

std::cout << "[" << path << "]" << std::endl;

is fine too, the point is checking if you're getting what you're
expecting or not.

This output is fine:

[folder/filename.ext]

these are not:

[folder/filename.ext ] <- probably a trailing tab character

[folder/filename.ext
] <- probably a new-line and/or a carriage-return trailing character
And I still need to make the
string identical to the manually typed string somehow.

The target is that, but the questions to answer should be: "Why am I
_not_ getting what I expect? Which part of the program is messing up
the 'path' string?"

By the way, your quoting is messed up. Fix it for the future.

Also, why are you using fopen and not filestreams? Just out of
curiosity.
 
F

Francesco S. Carta

[snip]
I don't understand is : ->  "  <- a delimiter?

Yes, it is.

std::cout << "[" << path << "]" << std::endl;

is fine too, the point is checking if you're getting what you're
expecting or not.

This output is fine:

[folder/filename.ext]

these are not:

[folder/filename.ext    ] <- probably a trailing tab character

[folder/filename.ext
] <- probably a new-line and/or a carriage-return trailing character
                        And I still need to make the
string identical to the manually typed string somehow.

The target is that, but the questions to answer should be: "Why am I
_not_ getting what I expect? Which part of the program is messing up
the 'path' string?"

Consider also that you might be having a messed up 'path' string from
starters.
How are you filling it in first place?
 
L

LR

red said:
But how do I find out what the last char is? There is no trim function in
the stl but I have found: [redacted]
But even if I pass my string to this function it still has size 78
afterwards! Any ideas?

Delimiters!!!!!!

std::cout << '"' << path << '"' << std::endl;

I think that delimiters can be useful, but are sometimes misleading.
Better to dump the string as octal, hex or decimal and see what's in it.

This code may be useful as a point of departure. Please watch for any
portability issues I missed.

#include <iostream>
#include <sstream>
#include <iomanip>
#include <string>
#include <limits.h>

std::string dump(const std::string &s) {
std::stringstream o;
o << (unsigned long int)(s.size()) << std::endl;
o << "*" << s << "*" << std::endl;

// if the number of bits in char
// isn't a multiple of 4, fix this
static const size_t hexWidth = CHAR_BIT/4;
for(std::string::const_iterator i=s.begin(); i!=s.end(); i++) {
o
<< std::hex
<< std::setfill('0')
<< std::setw(hexWidth)
<< int(*i)
<< " "
<< std::dec; // not the best place for this
}
return o.str();
}

int main() {
const std::string backspace(1,8); // YMMV, this is for ascii
const std::string nullchar(1,0);
const std::string hw("hello world");
const std::string ick = hw + nullchar + backspace;

std::cout << "hw\n" << dump(hw) << std::endl;
std::cout << "ick\n" << dump(ick) << std::endl;
}
On my computer the output was:
hw
11
*hello world*
68 65 6c 6c 6f 20 77 6f 72 6c 64
ick
13
*hello world*
68 65 6c 6c 6f 20 77 6f 72 6c 64 00 08

LR
 
T

Thomas J. Gritzan

Perhaps there are trailing unprintable characters in the path string
passed to your function? Try printing the size of your string.
Good point and you are right:


Before:

pathB size = 78


After:

pathA size = 77

But how do I find out what the last char is? There is no trim function
in the stl but I have found:

void trim2(string& str)
{
string::size_type pos = str.find_last_not_of(' ');
if(pos != string::npos) {
str.erase(pos + 1);
pos = str.find_first_not_of(' ');
if(pos != string::npos) str.erase(0, pos);
}
else str.erase(str.begin(), str.end());
}

But even if I pass my string to this function it still has size 78
afterwards! Any ideas?

That depends on how do you construct your string. Show us that code.

But I guess that you put a C-style string into that std::string, and
those are delimited by a final '\0' character. The standard way to get
its size is by strlen. You can resize your string using the resize
member function to this size:

void trim_cstring(string& str)
{
str.resize( strlen(&str[0]) );
}

By the way, it is good style to pass variables you don't want to change
in a function by const reference:

unsigned int LoadFile(std::string const& path);

This way, the user of this function knows that you don't change the
string and can rely on it. Additionally, you can pass a temporary string
to the function which you can't if the parameter is a non-const reference.
 
B

Balog Pal

"Thomas J. Gritzan"
But I guess that you put a C-style string into that std::string, and
those are delimited by a final '\0' character. The standard way to get
its size is by strlen. You can resize your string using the resize
member function to this size:

void trim_cstring(string& str)
{
str.resize( strlen(&str[0]) );
}

:-(

operator[] of std::string is there to access individual elements.
The thing you write has several problems:

- if string is empty, it is out of bounds access, undefined behavior
- nothing states that the storage is continous, and the other bytes are
stored in the following bytes
- especially nothing states that the string will hold anything but the
string content's characters -- without terminating 0 that is required by
strlen()

If ypu want to call strlen, do it on str.c_str() that is there exactly to
provide an old C-compatible string.
 
T

Thomas J. Gritzan

Balog said:
"Thomas J. Gritzan"
But I guess that you put a C-style string into that std::string, and
those are delimited by a final '\0' character. The standard way to get
its size is by strlen. You can resize your string using the resize
member function to this size:

void trim_cstring(string& str)
{
str.resize( strlen(&str[0]) );
}

:-(

operator[] of std::string is there to access individual elements.
The thing you write has several problems:

- if string is empty, it is out of bounds access, undefined behavior

Correct, but are you sure that calling c_str on an empty string isn't UB?
- nothing states that the storage is continous, and the other bytes are
stored in the following bytes

True, but practically, it is, and the next standard guarantees it.
- especially nothing states that the string will hold anything but the
string content's characters -- without terminating 0 that is required by
strlen()

My point is that the OP did store a C-style string with terminating 0
and some trailing bytes in the string, so that strlen would calculate
the C-style substring up to this terminating 0. But c_str would ensure
that there really is a '\0' somewhere so that calling strlen is correct
in any way.
If ypu want to call strlen, do it on str.c_str() that is there exactly
to provide an old C-compatible string.

Agreed.
 
F

Francesco S. Carta

Balog Pal schrieb:


"Thomas J. Gritzan"
But I guess that you put a C-style string into that std::string, and
those are delimited by a final '\0' character. The standard way to get
its size is by strlen. You can resize your string using the resize
member function to this size:
void trim_cstring(string& str)
{
  str.resize( strlen(&str[0]) );
}

operator[] of std::string is there to access individual elements.
The thing you write has several problems:
- if string is empty, it is out of bounds access, undefined behavior

Correct, but are you sure that calling c_str on an empty string isn't UB?

No, it isn't. You should be using c_str() without any kind of worry.
When the string is empty, c_str() will return a char[1] array
containing just the null character.
 
F

Francesco S. Carta

red said:
But how do I find out what the last char is? There is no trim function in
the stl but I have found: [redacted]
But even if I pass my string to this function it still has size 78
afterwards! Any ideas?
Delimiters!!!!!!

std::cout << '"' << path << '"' << std::endl;

I think that delimiters can be useful, but are sometimes misleading.
Better to dump the string as octal, hex or decimal and see what's in it.

Very good advice, I overlooked it. Thanks for pointing it out.

Reading your code with the CHAR_BIT thingie made me also realize that
I had a misunderstanding about sizeof(char), and I corrected it,
thanks a lot.
 
F

Francesco S. Carta

A somewhat refined / improved version of the facilities suggested so
far by other posters, used to trim strings at the NULL character and
to inspect them, in general:

-------
#include <iostream>
#include <string>
#include <sstream>
#include <limits>
#include <iomanip>

void trim_c_str(std::string* str) {
size_t pos = str->find('\0');
if (pos != std::string::npos) str->erase(pos);
}

void dump_as_unsigned(std::eek:stream& os,
const std::string& str,
size_t w = 0,
const std::string& lead_sep = " ") {
for (size_t i = 0, e = str.size(); i < e; ++i) {
os << lead_sep << std::setw(w) << unsigned(str);
}
os << std::endl;
}

void dump_chars(std::eek:stream& os, const std::string& str, size_t
spaces = 0) {
if (spaces) {
for (size_t i = 0, e = str.size(); i < e; ++i) {
os << std::string(spaces, ' ') << str;
}
} else {
os << str;
}
os << std::endl;
}

std::string string_details(const std::string& str) {
static std::eek:stringstream oss;
static size_t w = 0;

if (!w) {
oss << std::hex << unsigned(std::numeric_limits<char>::max());
oss << std::setfill('0') << std::uppercase;
w = oss.str().size();
}

oss.str("");
oss << "---" << std::endl;
oss << "As string:" << std::endl;
oss << " \"" << str << "\", ";
oss << str.size() << " chars" << std::endl;

dump_chars(oss, str, w);
dump_as_unsigned(oss, str, w);

std::string c_str = str.c_str();

if (c_str != str) {
oss << std::endl;
oss << "As c_str():" << std::endl;
oss << " \"" << c_str << "\", ";
oss << c_str.size() << " chars" << std::endl;

dump_chars(oss, c_str, w);
dump_as_unsigned(oss, c_str, w);

} else {
oss << "[ string == c_str(), no NULL characters ]";
oss << std::endl;
}
oss << "---" << std::endl;
return oss.str();
}

int main() {

std::string s = std::string("Here ->") + '\0' + "<- is NULL";

std::cout << "Before trimming:" << std::endl;
std::cout << string_details(s) << std::endl;

trim_c_str(&s);

std::cout << "After trimming:" << std::endl;
std::cout << string_details(s) << std::endl;
}
-------

Output:

-------
Before trimming:
---
As string:
"Here -> <- is NULL", 12 chars
H e r e - > < - i s N U L L
48 65 72 65 20 2D 3E 00 3C 2D 20 69 73 20 4E 55 4C 4C

As c_str():
"Here ->", 7 chars
H e r e - >
48 65 72 65 20 2D 3E
---

After trimming:
---
As string:
"Here ->", 7 chars
H e r e - >
48 65 72 65 20 2D 3E
[ string == c_str(), no NULL characters ]
 
F

Francesco S. Carta

void trim_c_str(std::string* str) {
    size_t pos = str->find('\0');
    if (pos != std::string::npos) str->erase(pos);

}

Uhm... I think this is the right occasion to ask for opinions about
this.

Here above I'm using a pointer without checking for it being null or
not.

Actually, that wasn't a mistake, I really meant to write that.

But obviously, if a client passes a nullpointer to that function, the
program crashes - in my view, the problem would be on the client's
side, but I wonder what's your opinion and eventually the most widely
used custom.

Three options:

-------
// #1
void foo(sometype* ptr) {
/*
dereference ptr without checking it for being null,
blame the client for passing a nullpointer
*/
}

// #2
void foo(sometype* ptr) {
if(ptr) {
/*
dereference ptr only if not null,
forgive the client when passing a nullpointer
*/
}
}

// #3
void foo(sometype& ref) {
/*
force the client to pass a non-const reference
*/
}
-------

Actually, I dislike both option #2 (buggy client calls won't be
punished) and #3 (client calls will not make it evident that foo can
modify the passed object).

I'll be glad to read others' opinions about these points.
 
B

Bo Persson

Francesco said:
Uhm... I think this is the right occasion to ask for opinions about
this.

Here above I'm using a pointer without checking for it being null or
not.

Actually, that wasn't a mistake, I really meant to write that.

But obviously, if a client passes a nullpointer to that function,
the program crashes - in my view, the problem would be on the
client's side, but I wonder what's your opinion and eventually the
most widely used custom.

Three options:

-------
// #1
void foo(sometype* ptr) {
/*
dereference ptr without checking it for being null,
blame the client for passing a nullpointer
*/
}

// #2
void foo(sometype* ptr) {
if(ptr) {
/*
dereference ptr only if not null,
forgive the client when passing a nullpointer
*/
}
}

// #3
void foo(sometype& ref) {
/*
force the client to pass a non-const reference
*/
}
-------

Actually, I dislike both option #2 (buggy client calls won't be
punished) and #3 (client calls will not make it evident that foo can
modify the passed object).

In this case, option 3 solves the problem at compile time, which is an
advantage. The client must provide a reference.

It also IS obvious that the function modifies its parameter, if you
just don't call it foo. The call

remove_trailing_stuff_from(filename);

seems like it could modify filename. :)


Bo Persson
 
F

Francesco S. Carta

In this case, option 3 solves the problem at compile time, which is an
advantage. The client must provide a reference.

It also IS obvious that the function modifies its parameter, if you
just don't call it foo. The call

remove_trailing_stuff_from(filename);

seems like it could modify filename. :)

Even if I fail to see the dereference of an eventual null pointer as a
problem of my function - that's still a problem of the caller for me -
I understand your point of view.

I really like when program details are primarily conveyed by syntax -
eventually also by naming convention, when appropriate.

Naming a function as "foo" is obviously silly - 'twas an example.

The case you proposed is trivial and obviously fine, passing its
argument by references.

Also passing streams by reference seems fine, since it seems to be
common custom.

I was thinking about multi-argument, somewhat more complex function
calls.

Imagine that I have to move some messages from one queue to two other
queues, depending on some message's attribute.

I would declare all arguments as pointers and I would call the
function in this way:

-------
dispatch(&msg_queue, &audio_queue, &video_queue);
-------

How would you name that function in order to convey the same meaning
in the function call, that is, that all passed objects could be
modified by the function?

OK, maybe that's a silly example, and the variable names are so
obvious that...
-------
dispatch(msg_queue, audio_queue, video_queue);
-------
....would be equally clear, but having the ampersands there in the
function call seems to me as the best way to express the function's
behavior.

Moreover, I was thinking about functions that modify only some of
their arguments.

Whatever. Ten heads, ten hats, that's fine, I realize that all of this
would be strictly tied to project/team conventions.
 
B

Balog Pal

"Thomas J. Gritzan"
void trim_cstring(string& str)
{
str.resize( strlen(&str[0]) );
}

:-(

operator[] of std::string is there to access individual elements.
The thing you write has several problems:

- if string is empty, it is out of bounds access, undefined behavior

Correct, but are you sure that calling c_str on an empty string isn't UB?
Yea.
- nothing states that the storage is continous, and the other bytes are
stored in the following bytes

True, but practically, it is, and the next standard guarantees it.

The next standard possibly evolves in that direction, but it was NOT the
plan in the first place (unlike for std::vector, where the memory req was
just forgotten in the first standard text and was fixed in TC1). string was
supposed to work being split in pieces internally.
My point is that the OP did store a C-style string with terminating 0

Maybe he did, it is a legal action for string and can be done easily through
its interface -- it's even easy to do by using the range constructor. Once
in the string it can be checked using the legal interface ways, i.e.
!str.empty() && '0' == *str.back() or strlen(str.c_str()) == str.size() or
many others already suggested. Taking address of elements in the string is
simply bad.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,997
Messages
2,570,241
Members
46,831
Latest member
RusselWill

Latest Threads

Top