0

Imagine having a label that after created updates x amount of times / sec. The text of the label is given as a format-specifier text (ala printf), and any arguments for the format-specifier is updated on redraw, because the arguments for the format specifier is pointers to their respective values. Does any variant of sprintf work like this?

The code would work something like this:

/* client */
createLabel("Value is %f", &myFloatValue);

I haven't quite figured out a way to do this yet, does anyone have any ideas? I guess one could parse the format text, retrieve the pointers (and types), and store them as some object in a list, where you later could reprint the text and maybe delegate the formatting to the objects themselves, passing them only a textbuffer.. hmmm

Btw, the interface is C, but the host is C++.

Okay i got a "working" prototype, but it's written mainly in assembler. Anyway it demonstrates the supposed use of the api. Can anyone see a portable way to do this / have a better idea for the implementation? It's pretty large so i'm posting it on pastebin: http://pastebin.com/H8ZpWb4u

Shaggi
  • 1,121
  • 1
  • 9
  • 31
  • C doesn't have references. If you have a pointer to a value, de-reference the pointer, and pass that to `sprintf`. – David Heffernan Jul 29 '13 at 18:48
  • It wasn't meant litteraly. – Shaggi Jul 29 '13 at 18:49
  • Is `createLabel` supposed to be varargs, like `sprintf`, or does it take predefined argument types? – Barmar Jul 29 '13 at 18:49
  • 2
    My point stands. `sprintf` wants values, so send it values. If you have pointers to values, de-reference the pointers. – David Heffernan Jul 29 '13 at 18:51
  • You might have some luck with `ncurses`, but ultimately you'll need to create most of the functionality yourself – Drew McGowen Jul 29 '13 at 18:52
  • 1
    I don't quite understand the question, but others do, so I think I must be dense. – Jiminion Jul 29 '13 at 19:09
  • @barmar varargs, see edit david: it doesn't really work that way... to others, sorry if i really suck at explaining myself but i managed to create a working prototype now to demonstrate at least. – Shaggi Jul 30 '13 at 17:54
  • This isn't really feasible. C has functions like `vasprintf` and `vfprintf`, which allow a varargs function to call `printf`-family functions. But all they can do is pass on their variable argument list, not modify it or construct a new one. See http://stackoverflow.com/questions/14705920/passing-an-array-as-parameters-to-a-vararg-function for instance. – Barmar Jul 30 '13 at 20:25

3 Answers3

0

So your createLabel interface would store the format string, along with the addresses of the variables you're wanting to display within the string. Then just use standard old sprintf to reformat the text. Just be careful with those pointers to the data, and make sure you invalidate them when necessary.

I'm not really sure what the problem is. What else are you looking for? sprintf is capable of doing what you want, but you're going to have to track the format string and variable addresses yourself.

  • Yeah pretty much, but it still leaves a problem. How do i call sprintf with an arbitrary amount of arguments with an arbitrary size? I can't just dereference them, because obv. sizeof(*mycharptr) != sizeof(*mydoubleptr) – Shaggi Jul 30 '13 at 05:44
  • @Shaggi: Make `createLabel()` a variadic function like `sprintf()` is: `createLabel(const char * format, ...);` – alk Jul 30 '13 at 06:50
  • @alk You're misunderstanding. Say i have a void * list of arguments of size n. How do i call sprintf with these? (sprintf(buf, fmt, (type) list[n], ... (type) list[0]) for n -> 0) – Shaggi Jul 30 '13 at 13:57
  • @Shaggi: You can not, as dereferencing a `void *` provokes undefined behaviour. The only way to go is to re-implement `printf()`'s format-string parser, to pull the types from it, then cast the void-pointers accordingly, just only to pass the results to `sprintf()` again. I'd say there is a conceptual problem. Or should it be the case that the info of the types behind the void-pointers is available from another source but from the format-string? – alk Jul 30 '13 at 15:31
  • @alk. Yes, i spend some time writing a (horrible) prototype implementation, ill post it in a minute. – Shaggi Jul 30 '13 at 17:01
0

Okay i suddenly got an idea .. stringstream + templated polymorphism. I ended up writing the thing in C++ in 5 mins, and at the very least it's a huge improvement.

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

class CBaseValue 
{
public:
    virtual void toString(std::stringstream & buf) = 0;
};

template< typename T >
    class CValue : public CBaseValue
    {
        typedef T type;
        typedef T * ptr_type;
        type * val;

    public:
        CValue(void * val) 
        {
            this->val = reinterpret_cast<ptr_type>(val);
        }

        CValue(type * val) : val(val) {}

        virtual void toString(std::stringstream & buf) {
            buf << *val;
        }
    };

class CLabel 
{

    std::stringstream ss;
    std::vector<CBaseValue *> valueList;
    std::string format;

public:
    CLabel() {};
    void reset() {
        format.clear();
        ss.str("");
        for(unsigned i = 0; i < valueList.size(); i++) {
            delete valueList[i];
        }
        valueList.clear();
    }
    void setFormat(const char * fmt, ...) {

        reset();
        format = fmt;
        va_list args;
        va_start(args, fmt);

        for(unsigned i = 0; i < format.size(); ++i) {
            if(format[i] == '%') {
                ++i;
                switch(fmt[i])
                {
                case 'd':
                    valueList.push_back(new CValue<unsigned int>( va_arg(args, void *) ));
                    break;
                case 'f':
                    valueList.push_back(new CValue<float>( va_arg(args, void *) ));
                    break;
                }
            }
        }
        va_end(args);

    }

    std::string get() {
        ss.str("");
        unsigned count(0);
        for(unsigned i = 0; i < format.size(); i++) {
            if(format[i] == '%') {
                i++; // ignore type specifiers, already polymorphically solved
                valueList[count++]->toString(ss);
            } else {
                ss << format[i];
            }
        }
        return ss.str();
    }
    ~CLabel() {
        reset();
    }
};


int main() {

    int test = 2;
    float val = 3.14f;

    CLabel myLabel;
    myLabel.setFormat("Stringstream test, float: %f, and an int: %d \n", &val, &test);
    std::cout << myLabel.get();
    test = 3;
    std::cout << myLabel.get();

    system("pause");
}
Shaggi
  • 1,121
  • 1
  • 9
  • 31
0

You could do something relatively simple with std::bind or boost::bind. I'll leave it as an exercise on how to massage a C interface on top of this.

#include <functional>

int main() {
  int test = 2;
  float val = 3.14f;

  std::function<int()> label = std::bind(
      printf,
      "Stringstream test, float: %f, and an int: %d \n",
      std::ref(val),
      std::ref(test));

  label();
  test = 3;
  label();
}
Bill Lynch
  • 80,138
  • 16
  • 128
  • 173
  • Clean and nice idea. I'll think about it during the day, and return later, but i suspect there may be a problem with converting an arbitrary amount of arguments (runtime) from a variadic function into a bind() construct. – Shaggi Jul 31 '13 at 05:50
  • @Shaggi: And more so with the loss of type information with va_args. – Bill Lynch Jul 31 '13 at 14:52