Why are some statements skipped in gdb? - gdb

I'm debugging this code :
len = NGX_SYS_NERR * sizeof(ngx_str_t);
ngx_sys_errlist = malloc(len);
if (ngx_sys_errlist == NULL) {
goto failed;
}
for (err = 0; err < NGX_SYS_NERR; err++) {
But in gdb if (ngx_sys_errlist == NULL) { is skipped directly:
(gdb)
59 ngx_sys_errlist = malloc(len);
(gdb) n
64 for (err = 0; err < NGX_SYS_NERR; err++) {
I also have experienced this before,but never knows the reason,anyone knows?
Is it a bug?
UPDATE
0x000000000041be9d <ngx_strerror_init+0>: mov %rbx,-0x30(%rsp)
0x000000000041bea2 <ngx_strerror_init+5>: mov %rbp,-0x28(%rsp)
0x000000000041bea7 <ngx_strerror_init+10>: mov %r12,-0x20(%rsp)
0x000000000041beac <ngx_strerror_init+15>: mov %r13,-0x18(%rsp)
0x000000000041beb1 <ngx_strerror_init+20>: mov %r14,-0x10(%rsp)
0x000000000041beb6 <ngx_strerror_init+25>: mov %r15,-0x8(%rsp)
0x000000000041bebb <ngx_strerror_init+30>: sub $0x38,%rsp
0x000000000041bebf <ngx_strerror_init+34>: mov $0x840,%edi
0x000000000041bec4 <ngx_strerror_init+39>: callq 0x402388 <malloc#plt>
0x000000000041bec9 <ngx_strerror_init+44>: mov %rax,0x26e718(%rip) # 0x68a5e8 <ngx_sys_errlist>
0x000000000041bed0 <ngx_strerror_init+51>: mov $0x840,%r12d
0x000000000041bed6 <ngx_strerror_init+57>: test %rax,%rax
0x000000000041bed9 <ngx_strerror_init+60>: je 0x41bf56 <ngx_strerror_init+185>
0x000000000041bedb <ngx_strerror_init+62>: mov $0x0,%r13d
0x000000000041bee1 <ngx_strerror_init+68>: mov $0x0,%r14d
0x000000000041bee7 <ngx_strerror_init+74>: mov $0x0,%r15d
0x000000000041beed <ngx_strerror_init+80>: mov %r13d,%edi
0x000000000041bef0 <ngx_strerror_init+83>: callq 0x402578 <strerror#plt>
UPDATE
Nobody else ever met the same thing in using gdb? It happens to me frequently when debugging.

Most likely the two statements were optimized into a single set-and-test expression, which then can't be decomposed into the original two lines. The generated pseudocode is likely to be something like
call _malloc
jz _failed
mov acc, _ngx_sys_errlist
where the test now happens before the assignment; do you let the source level trace go backwards to reflect this?

please check,
a) if you are debugging release build (if there exists one)
b) if your source file is modified
if you still have the issue, please provide the details (Complier with version, degugger version , platform and code ...)

Related

in c++ is there any way specialise a function template for specific values of arguments

I have a broadly used function foo(int a, int b) and I want to provide a special version of foo that performs differently if a is say 1.
a) I don't want to go through the whole code base and change all occurrences of foo(1, b) to foo1(b) because the rules on arguments may change and I dont want to keep going through the code base whenever the rules on arguments change.
b) I don't want to burden function foo with an "if (a == 1)" test because of performance issues.
It seems to me to be a fundamental skill of the compiler to call the right code based on what it can see in front of it. Or is this a possible missing feature of C++ that requires macros or something to handle currently.
Simply write
inline int foo(int a, int b)
{
if (a==1) {
// skip complex code and call easy code
call_easy(b);
} else {
// complex code here
do_complex(a, b);
}
}
When you call
foo(1, 10);
the optimizer will/should simply insert a call_easy(b).
Any decent optimizer will inline the function and detect if the function has been called with a==1. Also I think that the entire constexpr mentioned in other posts is nice, but not really necessary in your case. constexpr is very useful, if you want to resolve values at compile time. But you simply asked to switch code paths based on a value at runtime. The optimizer should be able to detect that.
In order to detect that, the optimizer needs to see your function definition at all places where your function is called. Hence the inline requirement - although compilers such as Visual Studio have a "generate code at link time" feature, that reduces this requirement somewhat.
Finally you might want to look at C++ attributes [[likely]] (I think). I haven't worked with them yet, but they are supposed to tell the compiler which execution path is likely and give a hint to the optimizer.
And why don't you experiment a little and look at the generated code in the debugger/disassemble. That will give you a feel for the optimizer. Don't forget that the optimizer is likely only active in Release Builds :)
Templates work in compile time and you want to decide in runtime which is never possible. If and only if you really can call your function with constexpr values, than you can change to a template, but the call becomes foo<1,2>() instead of foo(1,2); "performance issues"... that's really funny! If that single compare assembler instruction is the performance problem... yes, than you have done everything super perfect :-)
BTW: If you already call with constexpr values and the function is visible in the compilation unit, you can be sure the compiler already knows to optimize it away...
But there is another way to handle such things if you really have constexpr values sometimes and your algorithm inside the function can be constexpr evaluated. In that case, you can decide inside the function if your function was called in a constexpr context. If that is the case, you can do a full compile time algorithm which also can contain your if ( a== 1) which will be fully evaluated in compile time. If the function is not called in constexpr context, the function is running as before without any additional overhead.
To do such decision in compile time we need the actual C++ standard ( C++20 )!
constexpr int foo( int a, int)
{
if (std::is_constant_evaluated() )
{ // this part is fully evaluated in compile time!
if ( a == 1 )
{
return 1;
}
else
{
return 2;
}
}
else
{ // and the rest runs as before in runtime
if ( a == 0 )
{
return 3;
}
else
{
return 4;
}
}
}
int main()
{
constexpr int res1 = foo( 1,0 ); // fully evaluated during compile time
constexpr int res2 = foo( 2,0 ); // also full compile time
std::cout << res1 << std::endl;
std::cout << res2 << std::endl;
std::cout << foo( 5, 0) << std::endl; // here we go in runtime
std::cout << foo( 0, 0) << std::endl; // here we go in runtime
}
That code will return:
1
2
4
3
So we do not need to go with classic templates, no need to change the rest of the code but have full compile time optimization if possible.
#Sebastian's suggestion works at least in the simple case with all optimisation levels except -O0 in g++ 9.3.0 on Ubuntu 20.04 in c++20 mode. Thanks again.
See below disassembly always calling directly the correct subfunction func1 or func2 instead of the top function func(). A similar disassembly after -O0 shows only the top level func() being called leaving the decision to run-time which is not desired.
I hope this will work in production code and perhaps with multiple hard coded arguments.
Breakpoint 1, main () at p1.cpp:24
24 int main() {
(gdb) disass /m
Dump of assembler code for function main():
6 inline void func(int a, int b) {
7
8 if (a == 1)
9 func1(b);
10 else
11 func2(a,b);
12 }
13
14 void func1(int b) {
15 std::cout << "func1 " << " " << " " << b << std::endl;
16 }
17
18 void func2(int a, int b) {
19 std::cout << "func2 " << a << " " << b << std::endl;
20 }
21
22 };
23
24 int main() {
=> 0x0000555555555286 <+0>: endbr64
0x000055555555528a <+4>: push %rbp
0x000055555555528b <+5>: push %rbx
0x000055555555528c <+6>: sub $0x18,%rsp
0x0000555555555290 <+10>: mov $0x28,%ebp
0x0000555555555295 <+15>: mov %fs:0x0(%rbp),%rax
0x000055555555529a <+20>: mov %rax,0x8(%rsp)
0x000055555555529f <+25>: xor %eax,%eax
25
26 X x1;
27
28 int b=1;
29 x1.func(1,b);
0x00005555555552a1 <+27>: lea 0x7(%rsp),%rbx
0x00005555555552a6 <+32>: mov $0x1,%esi
0x00005555555552ab <+37>: mov %rbx,%rdi
0x00005555555552ae <+40>: callq 0x55555555531e <X::func1(int)>
30
31 b=2;
32 x1.func(2,b);
0x00005555555552b3 <+45>: mov $0x2,%edx
0x00005555555552b8 <+50>: mov $0x2,%esi
0x00005555555552bd <+55>: mov %rbx,%rdi
0x00005555555552c0 <+58>: callq 0x5555555553de <X::func2(int, int)>
33
34 b=3;
35 x1.func(1,b);
0x00005555555552c5 <+63>: mov $0x3,%esi
0x00005555555552ca <+68>: mov %rbx,%rdi
0x00005555555552cd <+71>: callq 0x55555555531e <X::func1(int)>
36
37 b=4;
38 x1.func(2,b);
0x00005555555552d2 <+76>: mov $0x4,%edx
0x00005555555552d7 <+81>: mov $0x2,%esi
0x00005555555552dc <+86>: mov %rbx,%rdi
0x00005555555552df <+89>: callq 0x5555555553de <X::func2(int, int)>
39
40 return 0;
0x00005555555552e4 <+94>: mov 0x8(%rsp),%rax
0x00005555555552e9 <+99>: xor %fs:0x0(%rbp),%rax
0x00005555555552ee <+104>: jne 0x5555555552fc <main()+118>
0x00005555555552f0 <+106>: mov $0x0,%eax
0x00005555555552f5 <+111>: add $0x18,%rsp
0x00005555555552f9 <+115>: pop %rbx
0x00005555555552fa <+116>: pop %rbp
0x00005555555552fb <+117>: retq
0x00005555555552fc <+118>: callq 0x555555555100 <__stack_chk_fail#plt>
End of assembler dump.

Maintain x*x in C++

I have the following while-loop
uint32_t x = 0;
while(x*x < STOP_CONDITION) {
if(CHECK_CONDITION) x++
// Do other stuff that modifies CHECK_CONDITION
}
The STOP_CONDITION is constant at run-time, but not at compile time. Is there are more efficient way to maintain x*x or do I really need to recompute it every time?
Note: According to the benchmark below, this code runs about 1 -- 2% slower than this option. Please read the disclaimer included at the bottom!
In addition to Tamas Ionut's answer, if you want to maintain STOP_CONDITION as the actual stop condition and avoid the square root calculation, you could update the square using the mathematical identity
(x + 1)² = x² + 2x + 1
whenever you change x:
uint32_t x = 0;
unit32_t xSquare = 0;
while(xSquare < STOP_CONDITION) {
if(CHECK_CONDITION) {
xSquare += 2 * x + 1;
x++;
}
// Do other stuff that modifies CHECK_CONDITION
}
Since the 2*x + 1 is just a bit shift and an increment, the compiler should be able to optimize this fairly well.
Disclaimer: Since you asked "how can I optimize this code" I answered with one particular way to possibly make it faster. Whether the double + increment is actually faster than a single integer multiplication should be tested in practice. Whether you should optimize the code is a different question. I assume you have already benchmarked the loop and found it to be a bottleneck, or that you have a theoretical interest in the question. If you are writing production code that you wish to optimize, first measure the performance and then optimize where needed (which is probably not the x*x in this loop).
What about:
uint32_t x = 0;
double bound= sqrt(STOP_CONDITION);
while(x < bound) {
if(CHECK_CONDITION) x++
// Do other stuff that modifies CHECK_CONDITION
}
This way, you're getting rid of that extra computation.
I made a small benchmarking for Tamas Ionut and CompuChip answers and here are the results:
Tamas Ionut: 19.7068
The code of this method:
uint32_t x = 0;
double bound= sqrt(STOP_CONDITION);
while(x < bound) {
if(CHECK_CONDITION) x++
// Do other stuff that modifies CHECK_CONDITION
}
CompuChip: 20.2056
The code of this method:
uint32_t x = 0;
unit32_t xSquare = 0;
while(xSquare < STOP_CONDITION) {
if(CHECK_CONDITION) {
xSquare += 2 * x + 1;
x++;
}
// Do other stuff that modifies CHECK_CONDITION
}
with STOP_CONDITION = 1000000 and repeating the process 1000000 times
Environment:
Compiler : MSVC 2013
OS : Windows 8.1 - X64
Processor: Core i7-4510U
#2.00 GHZ
Release Mode - Maximize Speed (/O2)
I would say, optimization in readibility is better than optimization in Performance in your case since we are talking about a very small Performance optimization
The compliter can optimize a lot for you regarding Performance but readibility lies in the responsibility of the programmer
I believe Tamas Ionut solution is better than that of CompuChip because we only have x++ inside the for loop. However, a comparison between uint32_t and double will kill the deal. It would be more efficient if we use uint32_t for bound instead of using double. This approach has less problem with numerical overflow because x cannot be greater than 2^16 = 65536 if we want to have a correct x^2 value.
If we also do a heavy work in the loop then results obtained from both approach should be very similar, however, Tamas Ionut approach is more simple and easier to read.
Below is my code and the corresponding assembly code obtained using clang version 3.8.0 with -O3 flag. It is very clear from the assembly code that the first approach is more efficient.
using T = size_t;
void test1(const T stopCondition, bool checkCondition) {
T x = 0;
while (x < stopCondition) {
if (checkCondition) {
x++;
}
// Do something heavy here
}
}
void test2(const T stopCondition, bool checkCondition) {
T x = 0;
T xSquare = 0;
const T threshold = stopCondition * stopCondition;
while (xSquare < threshold) {
if (checkCondition) {
xSquare += 2 * x + 1;
x++;
}
// Do something heavy here
}
}
(gdb) disassemble test1
Dump of assembler code for function _Z5test1mb:
0x0000000000400be0 <+0>: movzbl %sil,%eax
0x0000000000400be4 <+4>: mov %rax,%rcx
0x0000000000400be7 <+7>: neg %rcx
0x0000000000400bea <+10>: nopw 0x0(%rax,%rax,1)
0x0000000000400bf0 <+16>: add %rax,%rcx
0x0000000000400bf3 <+19>: cmp %rdi,%rcx
0x0000000000400bf6 <+22>: jb 0x400bf0 <_Z5test1mb+16>
0x0000000000400bf8 <+24>: retq
End of assembler dump.
(gdb) disassemble test2
Dump of assembler code for function _Z5test2mb:
0x0000000000400c00 <+0>: imul %rdi,%rdi
0x0000000000400c04 <+4>: test %sil,%sil
0x0000000000400c07 <+7>: je 0x400c2e <_Z5test2mb+46>
0x0000000000400c09 <+9>: xor %eax,%eax
0x0000000000400c0b <+11>: mov $0x1,%ecx
0x0000000000400c10 <+16>: test %rdi,%rdi
0x0000000000400c13 <+19>: je 0x400c42 <_Z5test2mb+66>
0x0000000000400c15 <+21>: data32 nopw %cs:0x0(%rax,%rax,1)
0x0000000000400c20 <+32>: add %rcx,%rax
0x0000000000400c23 <+35>: add $0x2,%rcx
0x0000000000400c27 <+39>: cmp %rdi,%rax
0x0000000000400c2a <+42>: jb 0x400c20 <_Z5test2mb+32>
0x0000000000400c2c <+44>: jmp 0x400c42 <_Z5test2mb+66>
0x0000000000400c2e <+46>: test %rdi,%rdi
0x0000000000400c31 <+49>: je 0x400c42 <_Z5test2mb+66>
0x0000000000400c33 <+51>: data32 data32 data32 nopw %cs:0x0(%rax,%rax,1)
0x0000000000400c40 <+64>: jmp 0x400c40 <_Z5test2mb+64>
0x0000000000400c42 <+66>: retq
End of assembler dump.

A variation of fibonacci assembly x86, have to call it from a c++ main method, kind of lost at few parts

This is the fibonacci code,
unsigned int fib(unsigned int n)
{
if (n==1 || n ==2)
return 1;
else
return fib(n-2) + fib(n-1);
}
but instead for my code, I have to change formula to a new one,
f(n-2)/2 + f(n-1) * 2, so the sequence is 1, 2, 4, 9, 20, 44, 98, 218
I need to write a recursive function called Mobonacci in assembly to calculate the nth number in sequence, and also a main function in c++ that reads a positive number n, then cals mobonacci assembly function with parameter n, then print our result
So I'm kind of confused, do I write the function in assembly like I did below, then write a c++ function to call it? and how would guys change my code from fibonacci to the new formula? Here is my code, what do I need to change and did I need create new part that let the code read input? Also is my code too short? do I need add anything else?
.code
main PROC
mov ecx,0
push 4 ; calculate the nth fib
call Fib ; calculate fib (eax)
call WriteDec
call Crlf
exit
main ENDP
Fib PROC
add ecx,1
push ebp
mov ebp,esp
mov eax,[ebp+8] ; get n
cmp eax,2 ; n == 2?
je exception2
cmp eax,1 ; n == 1?
je exception2
dec eax
push eax ; Fib(n-1)
call fib
add eax,
jmp Quit
Exception2:
dec eax
Quit:
pop ebp ; return EAX
ret 4 ; clean up stack
Fib ENDP
END main
Depends on where you are trying to insert asm code into your c++ code...
For gcc/linux you can do something like :
//Simple example:
void *frame; /* Frame pointer */
__asm__ ("mov %%ebp,%0":"=r"(frame));
//Complicated example:
int foo(void) {
int joe=1234, fred;
__asm__(
" mov %1,%%eax\n"
" add $2,%%eax\n"
" mov %%eax,%0\n"
:"=r" (fred) /* %0: Out */
:"r" (joe) /* %1: In */
:"%eax" /* Overwrite */
);
return fred;
}
The important thing is to understand how to use your asm function in cpp.
You can find some useful things about this subject here : https://www.cs.uaf.edu/2011/fall/cs301/lecture/10_12_asm_c.html
About the second part of your question.
To multiple, you can use the command "mul" and to make a division "div".
So if you want to do f(n-1) * 2
You have to get you register %eax after the "call fib" and use mul.
Just have a look here:
http://www.tutorialspoint.com/assembly_programming/assembly_arithmetic_instructions.htm

Why are intermediate results of template recursion in object file?

While playing around with template recursion in D, I found that the intermediate results of the classical factorial are still in the object file. I suppose they are also in the executable...?
I can see that the actually executed code contains only the value (or a pointer to it) but:
Shouldn't there be a single mov statement without the intermediate data being saved for no reason?
This is the code:
int main()
{
static int x = factorial!(5);
return x;//factorial!(5);
}
template factorial(int n)
{
static if (n == 1)
const factorial = 1;
else
const factorial = n * factorial!(n-1);
}
and this is the output of obj2asm test.o:
( for your convenience: 1! = 1h, 2! = 2h, 3! = 6h, 4! = 18h, 5! = 78h )
FLAT group
;File = test_fac_01.d
extrn _main
public _deh_beg
public _deh_end
public _tlsstart
public _tlsend
public _D11test_fac_014mainFZi1xi
extrn _GLOBAL_OFFSET_TABLE_
public _Dmain
public _D11test_fac_0112__ModuleInfoZ
extrn _Dmodule_ref
public _D11test_fac_017__arrayZ
public _D11test_fac_018__assertFiZv
public _D11test_fac_0115__unittest_failFiZv
extrn _d_array_bounds
extrn _d_unittestm
extrn _d_assertm
.text segment
assume CS:.text
:
mov EAX,offset FLAT:_D11test_fac_0112__ModuleInfoZ[018h]#32
mov ECX,offset FLAT:_Dmodule_ref#32
mov RDX,[RCX]
mov [RAX],RDX
mov [RCX],RAX
ret
.text ends
.data segment
_D11test_fac_0112__ModuleInfoZ:
db 004h,000h,000h,0ffffff80h,000h,000h,000h,000h ;........
db 074h,065h,073h,074h,05fh,066h,061h,063h ;test_fac
db 05fh,030h,031h,000h,000h,000h,000h,000h ;_01.....
db 000h,000h,000h,000h,000h,000h,000h,000h ;........
dq offset FLAT:_D11test_fac_0112__ModuleInfoZ#64
.data ends
.bss segment
.bss ends
.rodata segment
.rodata ends
.tdata segment
_tlsstart:
db 000h,000h,000h,000h,000h,000h,000h,000h ;........
db 000h,000h,000h,000h,000h,000h,000h,000h ;........
.tdata ends
.tdata. segment
_D11test_fac_014mainFZi1xi:
db 078h,000h,000h,000h ;x...
.tdata. ends
.text._Dmain segment
assume CS:.text._Dmain
_Dmain:
push RBP
mov RBP,RSP
mov RAX,FS:[00h]
mov RCX,_D11test_fac_014mainFZi1xi#GOTTPOFF[RIP]
mov EAX,[RCX][RAX]
pop RBP
ret
nop
nop
nop
.text._Dmain ends
.data._D11test_fac_0117__T9factorialVi5Z9factorialxi segment
_D11test_fac_0117__T9factorialVi5Z9factorialxi:
db 078h,000h,000h,000h ;x...
.data._D11test_fac_0117__T9factorialVi5Z9factorialxi ends
.data._D11test_fac_0117__T9factorialVi4Z9factorialxi segment
_D11test_fac_0117__T9factorialVi4Z9factorialxi:
db 018h,000h,000h,000h ;....
.data._D11test_fac_0117__T9factorialVi4Z9factorialxi ends
.data._D11test_fac_0117__T9factorialVi3Z9factorialxi segment
_D11test_fac_0117__T9factorialVi3Z9factorialxi:
db 006h,000h,000h,000h ;....
.data._D11test_fac_0117__T9factorialVi3Z9factorialxi ends
.data._D11test_fac_0117__T9factorialVi2Z9factorialxi segment
_D11test_fac_0117__T9factorialVi2Z9factorialxi:
db 002h,000h,000h,000h ;....
.data._D11test_fac_0117__T9factorialVi2Z9factorialxi ends
.data._D11test_fac_0117__T9factorialVi1Z9factorialxi segment
_D11test_fac_0117__T9factorialVi1Z9factorialxi:
db 001h,000h,000h,000h ;....
.data._D11test_fac_0117__T9factorialVi1Z9factorialxi ends
.ctors segment
dq offset FLAT:#64
.ctors ends
.text._D11test_fac_017__arrayZ segment
assume CS:.text._D11test_fac_017__arrayZ
_D11test_fac_017__arrayZ:
push RBP
mov RBP,RSP
sub RSP,010h
mov RSI,RDI
mov RDI,offset FLAT:_D11test_fac_0112__ModuleInfoZ#64
call _d_array_bounds#PC32
nop
nop
.text._D11test_fac_017__arrayZ ends
.text._D11test_fac_018__assertFiZv segment
assume CS:.text._D11test_fac_018__assertFiZv
_D11test_fac_018__assertFiZv:
push RBP
mov RBP,RSP
sub RSP,010h
mov RSI,RDI
mov RDI,offset FLAT:_D11test_fac_0112__ModuleInfoZ#64
call _d_assertm#PC32
nop
nop
.text._D11test_fac_018__assertFiZv ends
.text._D11test_fac_0115__unittest_failFiZv segment
assume CS:.text._D11test_fac_0115__unittest_failFiZv
_D11test_fac_0115__unittest_failFiZv:
push RBP
mov RBP,RSP
sub RSP,010h
mov RSI,RDI
mov RDI,offset FLAT:_D11test_fac_0112__ModuleInfoZ#64
call _d_unittestm#PC32
leave
ret
.text._D11test_fac_0115__unittest_failFiZv ends
end
You shouldn't use templates when what you want is compile-time function execution. Just write the function as you would and call it in a static context.
int main()
{
static int x = factorial(5); // static causes CTFE
return x;
}
int factorial(int n)
{
if (n == 1)
return 1;
else
return n * factorial(n-1);
}
This won't result in any extra symbols because factorial is evaluated at compile time. There are no symbols other than factorial itself. Your template trick instantiates symbols to achieve the same effect, but it's not symbols you want.
Alternatively, if you still want to use templates, but don't want symbols then you can use manifest constants via enum.
template factorial(int n)
{
static if (n == 1)
enum factorial = 1;
else
enum factorial = n * factorial!(n-1);
}
Notice the change from const to enum. enum values are purely compile-time, so they produce no symbols or data in the object files.

MASM str and substr?

I'm currently coding an irc bot in asm
I have already done this once in C++, so I know how to solve most problems I encounter, but I need a substr()[*] function like the one seen in C++. I need the substr function to receive the server name from a PING request so I can respond with the corresponding PONG response
But I don't know how to implent it in MASM, I heard of something called macroassembling, It seems substr is often used in those functions
Does anyone have any idea how I can get my substr function to work
[*] string substr ( size_t pos = 0, size_t n = npos )
This is how I use the substr() funcion in C++:
if(data.find("PING :") != std::string::npos){
string pong = "PONG :" + data.substr( (data.find_last_of(":")+1), (data.find_last_of("\r")-1) );
SCHiMBot.Pong(pong); // Keep the connection alive!
}
Where data is a string holding all the information the server sends me, and SCHiMBot is a class I use to talk with the server
This code is c&p'ed directly out of a bot I coded, so it should be flawless
This really isn't nearly as easy to answer is it might initially seem. The problem is pretty simple: a function like substr doesn't really exist in isolation -- it's part of a string library, and to make it useful, you just about need to at least sketch out how the library as a whole fits together, how you represent your data, etc. I.e., substr creates a string, but to do so you need to decide what a string is.
To avoid that problem, I'm going to sort of ignore what you actually asked, and give a somewhat simpler answer that's more suited to assembly language. What you really need is to start with one buffer of data, find a couple of "markers" in that buffer, and copy what's in between those markers to a designated position in another buffer. First we need the code to do the "find_last":
; expects:
; ESI = address of buffer
; ECX = length of data in buffer
; AH = character to find
; returns:
; ESI = position of item
;
find_last proc
mov al, [esi+ecx]
cmp ah, al
loopnz find_last
ret
find_last endp
Now to copy the substring to the transmission buffer, we do something like this:
CR = 13
copy_substr proc
mov esi, offset read_buffer
mov ecx, bytes_read
mov ah, CR
call find_last ; find the carriage-return
mov edx, esi ; save its position
mov esi, offset read_buffer
mov ecx, bytes_read
mov ah, ':'
call find_last ; find the colon
inc esi ; point to character following colon
sub edx, esi ; get distance from colon+1 to CR
mov ecx, edx
; Now: ESI = address following ':'
; ECX = distance to CR
mov edi, (offset trans_buffer) + prefix_length
rep movsb ; copy the data
ret
copy_substr endp
data.substr( (data.find_last_of(":")+1)
The first parameter of substr is the starting position. If it is a value passed the last element of the string, out_of_range exception will be thrown. You should verify that this is not happening.
if(data.find("PING :") != std::string::npos)
{
size_t s1 = data.find_last_of(":");
size_t s2 = data.find_last_of("\r");
if (s1 != string::npos &&
s2 != string::npos &&
s1+1 < data.size())
{
string pong = "PONG :" + data.substr(s1+1, s2-1);
SCHiMBot.Pong(pong); // Keep the connection alive!
}
}