variable number of parameters passed to sscanf() at runtime

Discussion in 'C++' started by Andrew, May 21, 2006.

  1. Andrew

    Andrew Guest

    Hi,

    I have a scenario in which both the source and format string for a
    sscanf() call is selected *at runtime*. The number of format
    conversions/substitutions is known as well as the maximum length of
    each, so it's easy enough to allocate the memory required dynamically.

    The problem is, how do I call sscanf()?

    The only way to acheive this I can think of is to mess with the stack
    using embedded asm and call the function myself, but i'm unsure how I
    could make this reasonably safe and portable. What I really need is a
    function defined like this:

    int asscanf(const char* source, const char* fmt, void** storage, int
    storage_size);

    where storage is an array of void* pointers for storing the converted
    data.

    Has anybody come up against this problem before or suggest anyway to
    tackle it?

    -- Andrew
     
    Andrew, May 21, 2006
    #1
    1. Advertising

  2. Andrew wrote:
    > I have a scenario in which both the source and format string for a
    > sscanf() call is selected *at runtime*.


    An interpreter that contains printf-like (and scanf-like) functionality?
    I can think of no other scenario that would require it, at this time.

    > The number of format
    > conversions/substitutions is known as well as the maximum length of
    > each, so it's easy enough to allocate the memory required dynamically.
    >
    > The problem is, how do I call sscanf()?
    >
    > The only way to acheive this I can think of is to mess with the stack
    > using embedded asm and call the function myself, but i'm unsure how I
    > could make this reasonably safe and portable. What I really need is a
    > function defined like this:
    >
    > int asscanf(const char* source, const char* fmt, void** storage, int
    > storage_size);
    >
    > where storage is an array of void* pointers for storing the converted
    > data.
    >
    > Has anybody come up against this problem before or suggest anyway to
    > tackle it?



    Yes, and the way to deal with it is to write your own "sscanf" using
    string functions and istringstreams.

    V
    --
    Please remove capital As from my address when replying by mail
     
    Victor Bazarov, May 21, 2006
    #2
    1. Advertising

  3. Andrew

    Andrew Guest

    The scenario is a simple but quite lengthy text protocol with messages
    like so:

    1 <formatted text>\n
    2 <differently formatted text>\n
    ....

    The first number is used to identify (amongst over things) the format
    string used to extract further parameters from the rest of the string.
    The idea being this will mute the need to do sscanf() (or other such
    interpretation) calls in dozens of handler functions (there is atleast
    1 for each message type).
     
    Andrew, May 21, 2006
    #3
  4. Andrew

    Eric Jensen Guest

    "Andrew" <> wrote in message
    news:...
    > Hi,
    >
    > I have a scenario in which both the source and format string for a
    > sscanf() call is selected *at runtime*. The number of format
    > conversions/substitutions is known as well as the maximum length of
    > each, so it's easy enough to allocate the memory required dynamically.
    >
    > The problem is, how do I call sscanf()?
    >
    > The only way to acheive this I can think of is to mess with the stack
    > using embedded asm and call the function myself, but i'm unsure how I
    > could make this reasonably safe and portable.


    I'd say a asm block would be the shortest way to do this.
    I made a very fast sample that compiles in vc++ 7 that
    shows howto call a function inside a simple asm block.
    You could with a asm block have a loop that pushes
    the number of variables in your array onto the stack
    and then calls sscanf, and afterwards pop the pushed
    variables from the stack and continues.
    You can do this with very little knowledge of assembly.

    //eric

    -- FILE stdafx.h --
    #pragma once

    #include <iostream>
    #include <tchar.h>
    -- EOF --
    -- FILE asmtest.cpp --
    #include "stdafx.h"
    void testfunc(int i) {
    std::cout << "void testfunc(int i): " << i << std::endl;
    }
    void testfunc2(int i, int j) {
    std::cout << "void testfunc2(int i, int j): " << i << " " << j <<
    std::endl;
    }
    int _tmain(int argc, _TCHAR* argv[])
    {
    // calling with 1 arg
    int i = 10, j = 20;
    __asm {
    PUSH i
    CALL testfunc
    POP i
    }

    // calling with 2 args
    __asm {
    PUSH j // arg 2 - read about calling convensions if you wonder why arg 2
    is first ;)
    PUSH i // arg 1
    CALL testfunc2 // do the call
    POP i // remove from stack
    POP j // remove from stack
    }
    return 0;
    }

    -- EOF --
     
    Eric Jensen, May 21, 2006
    #4
  5. Andrew

    Andrew Guest

    I've started an implementation of "asscanf", it doesn't yet work (it's
    not even called). My assembler is rusty and i've never actually done
    any inline assembler in C before. Here it is, it compiles in GCC 3.4.5
    on Linux (I'm hoping to port to Win32 later but this is all for today).

    I would appreciate anybody steering me in the right direction if i'm
    going just plain wrong, and thank you for the replies so far.

    -- Andrew


    #include <iostream>
    #include <stdio.h>

    __attribute__((cdecl))
    void testfunc2(int j, int i) {
    std::cout << "void testfunc2(int i, int j): " << i << " " << j <<

    std::endl;
    }

    int asscanf(const char *str, const char *format,
    const int num_ptrs, const void** ptrs)
    {
    int i;
    int result;

    // Need to add moving of the stack pointers (increasing and
    // shrinking the stack)

    for(int i = num_ptrs; i > 0; i--)
    { asm("push %0;" :: "m" (ptrs)); }

    asm(
    "push %0;\n\t" // format
    "push %1;\n\t" // str
    "call sscanf;"
    : "=a" (result)
    : "m" (format), "m" (str)
    );

    for(int i = num_ptrs; i > 0; i--)
    { asm("pop;"); } // Discard everything I dumped on the stack

    return result;
    }

    int main(int argc, char* argv[])
    {
    int i = 10, j = 20;

    sscanf("54", "%i", &j);

    // calling with 2 args
    asm("push %0;\n\t" // push i
    "push %1;\n\t" // push j
    "call _Z9testfunc2ii;\n\t" // call 'testfunc2'
    "pop %1;\n\t" // pop j }
    "pop %0;\n\t" // pop i } cleanup
    :
    : "m" (i), "m" (j)
    :
    );

    return 0;
    }
     
    Andrew, May 21, 2006
    #5
  6. Andrew

    Eric Jensen Guest

    "Andrew" <> skrev i en meddelelse
    news:...
    > I've started an implementation of "asscanf", it doesn't yet work (it's
    > not even called). My assembler is rusty and i've never actually done
    > any inline assembler in C before. Here it is, it compiles in GCC 3.4.5
    > on Linux (I'm hoping to port to Win32 later but this is all for today).


    > I would appreciate anybody steering me in the right direction if i'm
    > going just plain wrong, and thank you for the replies so far.


    I never did any asm on linux or with gcc. However i just made the following
    code for fun in vc++7 (win32). It uses printf to print out an int array.
    Im posting it the hope that it can be any help to you.

    //eric

    /*
    This code prints out the address in memory of each integer
    in the array i[]. You can put as many integers into it as
    you wish just remember to adjust the elements variable
    */
    void PrintArrayAddrs(int *p, int s, int elements) {
    /*
    int *p - Pointer to last element in the array
    int s - the size of a single element
    int elements - the count of elements
    */
    int c, h;
    std::string strFormat;
    strFormat = "Address of each variable in array:\n";
    for (c=0, h=0; c<elements; c++, h++) {
    strFormat += "%p ";
    if (h == 7) {
    strFormat += "\n";
    h = 0;
    }
    }
    const char *format = strFormat.c_str();
    __asm {
    mov eax,elements
    mov ebx,p
    PU_LOOP:
    dec eax
    push ebx
    sub ebx,s
    cmp eax,0
    je _CALL
    jmp PU_LOOP
    _CALL:
    push format
    call printf
    mov eax,elements
    mov ebx,p
    PO_LOOP:
    dec eax
    pop ebx
    sub ebx,s
    cmp eax,0
    je DONE
    jmp PO_LOOP
    DONE:
    pop format
    }
    }
    int _tmain(int argc, _TCHAR* argv[])
    {
    int i[10] = {10,20,30,40,50,60,70,80,90,100}; // array to print addresses
    of with printf
    // this array could be floats or whatever just change the types
    int elements = 10;
    PrintArrayAddrs(&i[elements-1], sizeof(int),elements);
    system("pause");
    return 0;
    }
     
    Eric Jensen, May 21, 2006
    #6
  7. Andrew

    Eric Jensen Guest

    > for (c=0, h=0; c<elements; c++, h++) {
    > strFormat += "%p ";
    > if (h == 7) {
    > strFormat += "\n";
    > h = 0;
    > }
    > }


    a little bug fix :)

    if (h == 7) {
    strFormat += "\n";
    h = -1;
    }
     
    Eric Jensen, May 22, 2006
    #7
  8. Andrew

    Heinz Ozwirk Guest

    "Andrew" <> schrieb im Newsbeitrag news:...
    > Hi,
    >
    > I have a scenario in which both the source and format string for a
    > sscanf() call is selected *at runtime*. The number of format
    > conversions/substitutions is known as well as the maximum length of
    > each, so it's easy enough to allocate the memory required dynamically.
    >
    > The problem is, how do I call sscanf()?
    >
    > The only way to acheive this I can think of is to mess with the stack
    > using embedded asm and call the function myself, but i'm unsure how I
    > could make this reasonably safe and portable. What I really need is a
    > function defined like this:
    >
    > int asscanf(const char* source, const char* fmt, void** storage, int
    > storage_size);
    >
    > where storage is an array of void* pointers for storing the converted
    > data.
    >
    > Has anybody come up against this problem before or suggest anyway to
    > tackle it?


    Before you try something desparate, look for vsprintf.

    HTH
    Heinz
     
    Heinz Ozwirk, May 22, 2006
    #8
  9. Andrew

    Andrew Guest

    the vsprintf and vsscanf family use macro's to enable the compiler to
    generate machine code for calling functions with a variable but known
    number of arguments at _compile time_. The number of arguments for any
    specific call must be known at _compile time_. This is not what I am
    after.

    Or am I wrong??
     
    Andrew, May 22, 2006
    #9
  10. Andrew

    Guest

    Andrew wrote:
    > Hi,
    >
    > I have a scenario in which both the source and format string for a
    > sscanf() call is selected *at runtime*. The number of format
    > conversions/substitutions is known as well as the maximum length of
    > each, so it's easy enough to allocate the memory required dynamically.
    >
    > The problem is, how do I call sscanf()?


    Probably the best answer: Not. The second answer would probably be, in
    a loop,
    passing one argument at a time. for-loops can use a dynamical upper
    bound ;)

    Of course, since this is C++, someone will point out that streams will
    make
    it even simpler.

    HTH,
    Michiel Salters
     
    , May 22, 2006
    #10
  11. Andrew wrote:
    > Hi,
    >
    > I have a scenario in which both the source and format string for a
    > sscanf() call is selected *at runtime*. The number of format
    > conversions/substitutions is known as well as the maximum length of
    > each, so it's easy enough to allocate the memory required dynamically.
    >
    > The problem is, how do I call sscanf()?


    You don't, you use a stringstream:

    # include <sstream>
    # include <string>
    # include <iostream>

    int main()
    {
    std::string s = "just a 10 test";

    std::string s1, s2, s3;
    int i;

    std::istringstream iss(s);
    iss >> s1 >> s2 >> i >> s3;

    std::cout << s1 << s2 << s3 << i;
    }

    > The only way to acheive this I can think of is to mess with the stack
    > using embedded asm and call the function myself[...]


    NooOOo!

    > int asscanf(const char* source, const char* fmt, void** storage, int
    > storage_size);
    >
    > where storage is an array of void* pointers for storing the converted
    > data.


    No no no no!

    A combination of streams, containers, strings and perhaps boost::any
    will solve your problem.


    Jonathan
     
    Jonathan Mcdougall, May 22, 2006
    #11
  12. * Andrew:
    >
    > I have a scenario in which both the source and format string for a
    > sscanf() call is selected *at runtime*. The number of format
    > conversions/substitutions is known as well as the maximum length of
    > each, so it's easy enough to allocate the memory required dynamically.
    >
    > The problem is, how do I call sscanf()?
    >
    > The only way to acheive this I can think of is to mess with the stack
    > using embedded asm and call the function myself, but i'm unsure how I
    > could make this reasonably safe and portable. What I really need is a
    > function defined like this:
    >
    > int asscanf(const char* source, const char* fmt, void** storage, int
    > storage_size);
    >
    > where storage is an array of void* pointers for storing the converted
    > data.
    >
    > Has anybody come up against this problem before or suggest anyway to
    > tackle it?


    I don't see that there's any problem.

    You'll need to parse the format string anyway.

    So just scan one item at a time.

    --
    A: Because it messes up the order in which people normally read text.
    Q: Why is it such a bad thing?
    A: Top-posting.
    Q: What is the most annoying thing on usenet and in e-mail?
     
    Alf P. Steinbach, May 22, 2006
    #12
  13. Andrew

    Andrew Guest

    Andrew, May 22, 2006
    #13
  14. Andrew

    Andrew Guest

    Thanks, that is an enlightening approach.
     
    Andrew, May 22, 2006
    #14
  15. Jonathan Mcdougall, May 22, 2006
    #15
  16. Andrew

    Andrew Guest

    You're right, I do need to tokenize the format string because the
    formats are non-trivial. I'll reimplement a subset of the scanf
    functionality using standard functions.
     
    Andrew, May 22, 2006
    #16
  17. Eric Jensen wrote:
    > "Andrew" <> skrev i en meddelelse
    > news:...

    -snip-

    >>I would appreciate anybody steering me in the right direction if i'm
    >>going just plain wrong, and thank you for the replies so far.

    >
    >
    > I never did any asm on linux or with gcc. However i just made the following
    > code for fun in vc++7 (win32). It uses printf to print out an int array.
    > Im posting it the hope that it can be any help to you.
    >
    > //eric
    >
    > /*
    > This code prints out the address in memory of each integer
    > in the array i[]. You can put as many integers into it as
    > you wish just remember to adjust the elements variable
    > */
    > void PrintArrayAddrs(int *p, int s, int elements) {
    > /*
    > int *p - Pointer to last element in the array
    > int s - the size of a single element
    > int elements - the count of elements
    > */
    > int c, h;
    > std::string strFormat;
    > strFormat = "Address of each variable in array:\n";
    > for (c=0, h=0; c<elements; c++, h++) {
    > strFormat += "%p ";
    > if (h == 7) {
    > strFormat += "\n";
    > h = 0;
    > }
    > }
    > const char *format = strFormat.c_str();
    > __asm {
    > mov eax,elements
    > mov ebx,p
    > PU_LOOP:
    > dec eax
    > push ebx
    > sub ebx,s
    > cmp eax,0
    > je _CALL
    > jmp PU_LOOP
    > _CALL:
    > push format
    > call printf
    > mov eax,elements
    > mov ebx,p
    > PO_LOOP:
    > dec eax
    > pop ebx
    > sub ebx,s
    > cmp eax,0
    > je DONE
    > jmp PO_LOOP
    > DONE:
    > pop format
    > }
    > }
    > int _tmain(int argc, _TCHAR* argv[])
    > {
    > int i[10] = {10,20,30,40,50,60,70,80,90,100}; // array to print addresses
    > of with printf
    > // this array could be floats or whatever just change the types
    > int elements = 10;
    > PrintArrayAddrs(&i[elements-1], sizeof(int),elements);
    > system("pause");
    > return 0;
    > }


    How to make that work on g++?

    g++ -fasm-blocks asm.c
    asm.c: In function 'void PrintArrayAddrs(int*, int, int)':
    asm.c:13: error: 'string' is not a member of 'std'
    asm.c:13: error: expected `;' before 'strFormat'
    asm.c:14: error: 'strFormat' was not declared in this scope
    asm.c: At global scope:
    asm.c:49: error: '_TCHAR' has not been declared
    asm.c: In function 'int _tmain(int, int**)':
    asm.c:52: error: 'of' was not declared in this scope
    asm.c:52: error: expected `;' before 'with'
    asm.c:55: error: 'elements' was not declared in this scope


    Best regards / Med venlig hilsen
    Martin Jørgensen

    --
    ---------------------------------------------------------------------------
    Home of Martin Jørgensen - http://www.martinjoergensen.dk
     
    =?ISO-8859-1?Q?Martin_J=F8rgensen?=, May 22, 2006
    #17
  18. Martin Jørgensen wrote:
    > Eric Jensen wrote:
    >> "Andrew" <> skrev i en meddelelse
    >> news:...

    > -snip-
    >
    >>> I would appreciate anybody steering me in the right direction if i'm
    >>> going just plain wrong, and thank you for the replies so far.

    >>
    >>
    >> I never did any asm on linux or with gcc. However i just made the
    >> following code for fun in vc++7 (win32). It uses printf to print out
    >> an int array. Im posting it the hope that it can be any help to you.
    >>
    >> //eric
    >>
    >> /*
    >> This code prints out the address in memory of each integer
    >> in the array i[]. You can put as many integers into it as
    >> you wish just remember to adjust the elements variable
    >> */
    >> void PrintArrayAddrs(int *p, int s, int elements) {
    >> [..]
    >> __asm {
    >> [..]
    >> }
    >> }
    >> [..]

    >
    > How to make that work on g++?
    >
    > [..]


    Built-in assembly is (a) platform-specific, (b) is defined to begin with
    'asm' keyword, not '__asm' and (c) implementation-defined. If you need
    a platform-specific compiler-specific solution, please consider taking
    this conversation to a more appropriate newsgroup. Let's keep this about
    C++ and not about Linux, Windows, GCC, VC++, etc.

    V
    --
    Please remove capital 'A's when replying by e-mail
    I do not respond to top-posted replies, please don't ask
     
    Victor Bazarov, May 23, 2006
    #18
  19. Martin Jørgensen wrote:
    > Eric Jensen wrote:
    > > "Andrew" <> skrev i en meddelelse
    > > news:...

    > -snip-
    >
    > >>I would appreciate anybody steering me in the right direction if i'm
    > >>going just plain wrong, and thank you for the replies so far.

    > >
    > >
    > > I never did any asm on linux or with gcc. However i just made the following
    > > code for fun in vc++7 (win32). It uses printf to print out an int array.
    > > Im posting it the hope that it can be any help to you.
    > >
    > > //eric
    > >
    > > /*
    > > This code prints out the address in memory of each integer
    > > in the array i[]. You can put as many integers into it as
    > > you wish just remember to adjust the elements variable
    > > */
    > > void PrintArrayAddrs(int *p, int s, int elements) {
    > > /*
    > > int *p - Pointer to last element in the array
    > > int s - the size of a single element
    > > int elements - the count of elements
    > > */
    > > int c, h;
    > > std::string strFormat;
    > > strFormat = "Address of each variable in array:\n";
    > > for (c=0, h=0; c<elements; c++, h++) {
    > > strFormat += "%p ";
    > > if (h == 7) {
    > > strFormat += "\n";
    > > h = 0;
    > > }
    > > }
    > > const char *format = strFormat.c_str();
    > > __asm {
    > > mov eax,elements
    > > mov ebx,p
    > > PU_LOOP:
    > > dec eax
    > > push ebx
    > > sub ebx,s
    > > cmp eax,0
    > > je _CALL
    > > jmp PU_LOOP
    > > _CALL:
    > > push format
    > > call printf
    > > mov eax,elements
    > > mov ebx,p
    > > PO_LOOP:
    > > dec eax
    > > pop ebx
    > > sub ebx,s
    > > cmp eax,0
    > > je DONE
    > > jmp PO_LOOP
    > > DONE:
    > > pop format
    > > }
    > > }
    > > int _tmain(int argc, _TCHAR* argv[])
    > > {
    > > int i[10] = {10,20,30,40,50,60,70,80,90,100}; // array to print addresses
    > > of with printf
    > > // this array could be floats or whatever just change the types
    > > int elements = 10;
    > > PrintArrayAddrs(&i[elements-1], sizeof(int),elements);
    > > system("pause");
    > > return 0;
    > > }

    >
    > How to make that work on g++?


    How about searching a bit before asking?

    > g++ -fasm-blocks asm.c
    > asm.c: In function 'void PrintArrayAddrs(int*, int, int)':
    > asm.c:13: error: 'string' is not a member of 'std'
    > asm.c:13: error: expected `;' before 'strFormat'
    > asm.c:14: error: 'strFormat' was not declared in this scope


    # include <string>

    > asm.c: At global scope:
    > asm.c:49: error: '_TCHAR' has not been declared


    _TCHAR doesn't exist in standard C++. Make it a const char*.

    > asm.c: In function 'int _tmain(int, int**)':


    int main()

    > asm.c:52: error: 'of' was not declared in this scope
    > asm.c:52: error: expected `;' before 'with'


    Seriously?? The comment is on two lines! Wake up!

    > asm.c:55: error: 'elements' was not declared in this scope


    Probably because of the other errors.


    Jonathan
     
    Jonathan Mcdougall, May 23, 2006
    #19
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. dee
    Replies:
    9
    Views:
    529
    Joseph Byrns
    Apr 15, 2005
  2. Anand
    Replies:
    2
    Views:
    926
    Anand
    Sep 11, 2003
  3. John Friedland
    Replies:
    18
    Views:
    556
    Adam Warner
    Jul 12, 2006
  4. John Friedland
    Replies:
    11
    Views:
    521
    Nicholas Howe
    Jul 14, 2006
  5. Hans

    sscanf() parameters

    Hans, May 10, 2009, in forum: C Programming
    Replies:
    17
    Views:
    2,131
    CBFalconer
    May 11, 2009
Loading...

Share This Page