0

I've been trying to learn a bit about socket programming in C++ and so have been developing a basic IRC bot. I've got it connecting and working, but I'm having an issue determining who has joined the channel (i.e. was the JOIN command for the bot itself, for a random user, or for me).

I'm using CLion on Windows 7.

I've got the following code:

//determine if message is JOIN command
if(regex_search(line, match, std::regex(R"(:([^!]*)!.*?JOIN)")))
{
    //get the nickname of the bot (details.getNickName() shown in next code segment)
    const char *trueNick = details.getNickName();
    //reformat nickname because the following is returned from the method alone
    //0x10042d0f9 <_ZStL6ignore+119> "CPlusPlusBotTest"
    const char *nick = string(details.getNickName()).c_str();
    //get the name of the user who joined
    const char *name = match.str(1).c_str();
    //debugging
    std::cout << name << " - name\n";
    std::cout << nick << " - nick\n";
    //might not be the correct way to compare the two? but I'll sort that out later
    if(name != nick)
    {
        //welcome the user
        char param[1024];
        sprintf(param, "%s :Hello, %s", details.getChannel(), name);
        sendData("PRIVMSG", param);
    }
}

I am unsure why I get the excess "stuff" (I have no idea what it is) from my getter, as it's simply a case of returning a private variable:

const char* BotDetails::getNickName() { return nickName; }

Regardless, that's not my issue given I can get rid of it (despite it possibly being rather hacky).

My issue is that when I connect to the channel for testing, and I set a breakpoint on the line assigning trueNick so I can see what happens as I step through the program, the following occurs:

1) trueNick is assigned the value: 0x10042d0f9 <_ZStL6ignore+119> "CPlusPlusBotTest"

2) nick is assigned the value: "CPlusPlusBotTest"

3) name is assigned the value: "Seanharrs" and nick is assigned the value: "Seanharrs"

This means when my debug statements run, nick and name are the same value. I am not sure why nick is being reassigned to be the same value as name, this should not occur. It happens every time as well, not just for my name. I tried using char arrays and strings instead but to no avail. I also find it strange that trueNick is never affected, it's only these two variables. Any help is appreciated (even if it's just an alt/better way to check this rather than a fix, because it may well be just an oddity on my end that nobody else experiences).

Seanharrs
  • 66
  • 1
  • 7
  • You have some things similar to the common string stream problem happening: http://stackoverflow.com/questions/1374468/stringstream-string-and-char-conversion-confusion – chris Jan 09 '16 at 07:59
  • 2
    Stop using `char *`s, and use `std::string` instead. This will automagically solve a lot of your issues. – Mat Jan 09 '16 at 08:00
  • 3
    `const char *nick = string(details.getNickName()).c_str();` Won't using `nick` be UB? – Weak to Enuma Elish Jan 09 '16 at 08:02
  • I end up having to convert everything to char * for function involving sockets, though, @Mat – Seanharrs Jan 09 '16 at 08:08
  • 6
    Then convert at the last possible step only. – Ulrich Eckhardt Jan 09 '16 at 08:10
  • Also thanks @chris I seem to at least have name and nick differing now (but my temporary string variables in between show as the same thing in the CLion debugging interface). I'll keep working and see if it works. I struggled to search for similar questions on here due to having no idea what to search for :/ – Seanharrs Jan 09 '16 at 08:10
  • rule of thumb, never save the result of `c_str()` - only use it as argument to functions which will not themselves store it – M.M Jan 09 '16 at 08:13
  • Noted. I've not used C++ in a few years and never really used anything other than string so I'm still learning. Guess I'll just go back to using std::string as Mat suggested. – Seanharrs Jan 09 '16 at 08:21
  • 1
    @Seanharrs `char param[1024]; sprintf(param, "%s :Hello, %s", details.getChannel(), name);` Hope that the `details.getChannel()` function plus the other text is a string less than 1024 characters. Since you're using C++, use `std::ostringstream` to ensure there is no buffer overflow. – PaulMcKenzie Jan 09 '16 at 08:34
  • @Seanharrs - I'm trying to understand your code/description. How/where is `0x10042d0f9 <_ZStL6ignore+119> "CPlusPlusBotTest"` converted to `"CPlusPlusBotTest"` - it seems I'm missing something? – Support Ukraine Jan 09 '16 at 09:17
  • @StillLearning the value that is returned by `details.getNickName()` has the stuff at the beginning before the quotes start, but by converting to string then converting the result of that conversion to char* using `c_str()` gets rid of it. @PaulMcKenzie I will consider that. I was just using basic arrays to get it working, helping ensure it stays working comes later. :) – Seanharrs Jan 09 '16 at 09:28
  • I don't see that conversion happening. Example: `const char k[] = "0x10042d0f9 <_ZStL6ignore+119> \"CPlusPlusBotTest\""; const char* pTrue = k; const char* p = std::string(k).c_str(); std::cout << pTrue << std::endl; std::cout << p << std::endl; ` will just print the whole string twice. What am I missing... – Support Ukraine Jan 09 '16 at 09:37
  • @StillLearning it's not "0x10042d0f9 <_ZStL6ignore+119> \"CPlusPlusBotTest\"" it's 0x10042d0f9 <_ZStL6ignore+119> "CPlusPlusBotTest" - the 'string' itself is just "CPlusPlusBotTest", the extra stuff is not a part of it (so when I print it the 0x10... etc. disappears, but it exists when I look at the value contained in the variable in a debugger). – Seanharrs Jan 09 '16 at 10:16
  • @Seanharrs - oh.... that's just how your debugger shows the variable! It is not the value of the variable. Debuggers will typical show some extra stuff for each variable. Here I'll guess that `0x10042d0f9` is the memory address of the variable. `<_ZStL6ignore+119>`seems to refer to a location in the program but I'm not sure about that. The value is already what you want, i.e. `"CPlusPlusBotTest"` so you don't need the "conversion". `trueNick` is already what you want. – Support Ukraine Jan 09 '16 at 10:20
  • @Seanharrs - BTW - to compare the value of two char* strings you can't just use `==` Instead do `if (strcmp(name, nick) == 0)` – Support Ukraine Jan 09 '16 at 10:32

1 Answers1

2

This line causes undefined behavior:

const char *nick = string(details.getNickName()).c_str();

It will construct a temporary string object and return a pointer to the data. However, being temporary means that it will be destructed immediately and the pointer will be invalid.

EDIT:

It turned out that OP misunderstood the additional information displayed by the debugger and interpreted it as the value of the variable. After clarifying that, there is no need for the "conversion" which causes undefined behavior and the code can just be:

const char *nick = details.getNickName();
const char *name = match.str(1).c_str();
if( strcmp(name, nick) == 0 )
{
    //....
}

The example of undefined behavior is still shown below.

EXAMPLE CODE FOR THE UNDEFINED BEHAVIOR

Consider this code:

#include <iostream>
using namespace std;

int main() {
    const char t[] = "0x10042d0f9 <_ZStL6ignore+119> \"CPlusPlusBotTest\"";
    const char* pTrue = t;
    const char* p = std::string(t).c_str();
    std::cout << pTrue << std::endl;
    std::cout << p << std::endl;
    return 0;
}

it will output (rather: it may output):

0x10042d0f9 <_ZStL6ignore+119> "CPlusPlusBotTest"
0x10042d0f9 <_ZStL6ignore+119> "CPlusPlusBotTest"

(ideone.com used for this example and gave above output)

So from this you might think it was okay. (note however: I don't get the conversion mentioned by OP).

Now consider this code:

#include <iostream>
using namespace std;

int main() {
    const char tdemo[] = "Some demo text";   // Added this line
    const char t[] = "0x10042d0f9 <_ZStL6ignore+119> \"CPlusPlusBotTest\"";
    const char* pTrue = t;
    const char* p = std::string(t).c_str();
    const char* pdemo = std::string(tdemo).c_str();  // Added this line
    std::cout << pTrue << std::endl;
    std::cout << p << std::endl;
    return 0;
} 

it will output (rather: it may output)

0x10042d0f9 <_ZStL6ignore+119> "CPlusPlusBotTest"
Some demo text

(ideone.com used for this example and gave above output)

As you can see the value of *p changed "unexpectedly". It changed because the pointer was invalid in the sense that it pointed to memory that was freed already. The extra line

const char* pdemo = std::string(tdemo).c_str();  

caused the compiler to reuse that memory and consequently the value *p changed.

In other words - you have undefined behavior.

My guess is that your problem is inside details.getNickName();

It seems to me that the pointer returned is not pointing to the same test every time. Maybe it has some initialization problem so that it returns the wrong value the first time and then correct values afterwards.

The line causing undefined behavior can not do the conversion claimed by OP so it must be the return value from the function that changes.

Community
  • 1
  • 1
Support Ukraine
  • 42,271
  • 4
  • 38
  • 63
  • That makes sense, thanks. I see now why it was troublesome and will avoid using that method in the future. Still doesn't explain why the `0x10042d0f9 <_ZStL6ignore+119>` existed outside my 'string' variable though (it did not appear on std::cout << trueNick; but it appeared when I viewed the value of trueNick in a debugger, it was "outside" the 'string', but still affecting its value so it was considered different to "CPlusPlusBotTest", even though the only 'string' part of trueNick was "CPlusPlusBotTest") – Seanharrs Jan 09 '16 at 10:21
  • @Seanharrs - Read my comment under the question. `0x10042d0f9 <_ZStL6ignore+119>` does not exists!! It is just some additional information displayed by the debugger. It has nothing to do with the value of the string. – Support Ukraine Jan 09 '16 at 10:29
  • I assumed it would be "extra" info that has nothing to do with the value of the string, except it appeared to change the value because `0x10042d0f9 <_ZStL6ignore+119> "CPlusPlusBotTest"` was considered different to `"CPlusPlusBotTest"`. I assume this was just because I was comparing with `==`, though. Thanks again. :) and I didn't accept because I couldn't find the button :P I have now. – Seanharrs Jan 10 '16 at 10:56