Thread: Looking for thoughts on my new alternative to printf/scanf

  1. #1
    Registered User awsdert's Avatar
    Join Date
    Jan 2015
    Posts
    1,733

    Looking for thoughts on my new alternative to printf/scanf

    both printf and scanf have both baggage and an inflexible design, I learned that the hard way when implementing my own specifiers etc, I've now decide to start converting my existing implementations in favour of something new, this is what I've decided so far:

    Code:
    /* Originally I wanted to mimic the existing printf/scanf standards, the
     * implementing of it enlightened me to how inflexible & inefficient the design
     * was so now I declaring a new standard and ignoring printf/scanf for now
     *
     * Arguments will be read like this: %/.../`options`'scalers'type%
     *
     * %% Will as usual mean just print/parse the character %
     *
     * The options get enclosed in a string so they cannot get confused with the
     * type specifier, the same reason applies to the scalers that get enclosed in
     * a string also. The /.../ means read a string in for regular expression
     * parsing and apply the options in the /.../ to the regular expression, the
     * options will be the same as in javascript, namely:
     *
    d Generate indices for substring matches.
    g Global search.
    i Case-insensitive search.
    m Allows ^ and $ to match newline characters.
    s Allows . to match newline characters.
    u "Unicode"; treat a pattern as a sequence of Unicode code points.
    y Perform a "sticky" search that matches starting at the current position in the target string.
     *
     * NOTE: Regular expression parsing is a PLANNED fetaure for the official
     * release of paw, for now it is currently unimplemented
     *
     * The last option of a kind takes precedence over previous options, for
     * example "%`<|>`s%" would right align the output string if padding is added
     *
     * Where * is the part of the option the * can be substituted for a literal,
     * for example %`c010`s% means to read the character and pad the output/input
     * if it is not at least 8 characters wide %`c*`s% carries same meaning but
     * instead of just 1 pawd for the character being read from the va_list 2 are
     * read, the 1st for the character, the 2nd for how many characters to pad it
     * to
     *
     * For strings paw supports the following:
     *
     * s	Raw string
     * sv	Buffer string
     *
     * 'l'	pawls/pawlsv
     * 't'	pawts/pawtsv
     * 'k'	pawks/pawksv
     * '8l'	paw8ls/paw8lsv
     * '16l'paw16ls/paw16lsv
     * '32l'paw32ls/paw32lsv
     *
     * `?`	Replace unsupported characters with this pawzc character
     * `<`	Left aligns output if padding is added, default
     * `|`	Center aligns output if padding is added, padding on either side will
     * 	be equal even if it means more than the stated padding
     * `>`	Right aligns output if padding is added
     * `c*`	Read 2 values in, pad output/input with character c until it is at
     *	least * characters wide, the character is always read in as a pawd
     * `l*`	Read 2 values in, pad output/input with character c until it is at
     *	least * characters wide, the character is always read in as a pawzc
     * `^*` Repeat the output * times
     * `.*`	When printing this is the EXACT length of the string, pawlen etc are
     *	skipped in favour of this.
     *
     *	When parsing this is the max characters to fill the string with,
     *	default is 0, without defining this option your strings will always be
     *	empty unless it was into buffers that it can increase the size of
     * `#`	Prefix appropriatly, for example insert L" to the front if
     *	input/output is supposed to be pawls/pawlsv
     * '!'	Suffix appropriatly, for strings this just means append "
     *
     * For numbers the following is supported:
     *
     * b	Binary variant, default is pawu
     * d	Signed variant, default is pawd
     * u	Unsigned variant, default is pawu
     * o	Octal variant, default is pawu
     * x	Lowercase hexadecimal variant, default is pawu
     * x	Uppercase hexadecimal variant, default is pawu
     * p	Uppercase hexadecimal pointer, always void*, # is always on
     *
     * 'f'	pawfd
     * 'lf'	pawlfd
     * 'jf'	pawjfd
     * 'kf'	pawkfd
     * 'hh'	pawhhd/pawhhu
     * 'h'	pawhd/pawhu
     * 'l'	pawld/pawlu
     * 'll'	pawlld/pawllu
     * 'j'	pawjd/pawju
     * 'k'	pawkd/pawku
     * 'v'	pawvd/pawvu
     * '8'	paw8d/paw8u
     * ...	All the paw#d, paw#ld, paw#qd, paw#fd variants follow suit
     *
     * '+'	Same as standard printf definition, forces a sign for numbers
     * `?`	Replace unsupported digits with this pawzc character (L option only)
     * `<`	Left aligns output if padding is added, default
     * `|`	Center aligns output if padding is added, padding on either side will
     * 	be equal even if it means more than the stated padding
     * `>`	Right aligns output if padding is added
     * `c*`	Read 2 values in, pad output/input with character c until it is at
     *	least * characters wide, the character is always read in as a pawd
     * `l*`	Read 2 values in, pad output/input with character c until it is at
     *	least * characters wide, the character is always read in as a pawzc
     * `^*` Repeat the output * times
     * `.*`	When printing this is the EXACT length of the string, pawlen etc are
     *	skipped in favour of this.
     *
     *	When parsing this is the max characters to fill the string with,
     *	default is 0, without defining this option your strings will always be
     *	empty unless it was into buffers that it can increase the size of
     * `#`	Prefix appropriatly, for example a hexadeximal value will have 0x
     * 	prefixed, always active for pointers
     * '!'	Suffix appropriatly, for example a pawlu will have UL appended
     *
     * Planned to support on official release
     * `,`	Digits should be grouped into 1000s with locale defined separator,
     *	I don't have an implementation for this presently, so until I do there
     *	will be no beta of paw
     * `L`Use locales alternative digit representations, again I need to do some
     *	research and implement this before I declare paw in beta state
     *
     * */
    
    RAWPAW_API pawd pawio_outputv( PAWIO io, pawls args, va_list *va );
    RAWPAW_API pawd pawio_outputf( PAWIO io, pawls args, ... ) PAW_ATTR_HOT;
    
    RAWPAW_API pawd pawio_expectv( PAWIO io, pawls args, va_list *va );
    RAWPAW_API pawd pawio_expectf( PAWIO io, pawls args, ... ) PAW_ATTR_HOT;
    What other options do you think would be desired in a new alternative? I'm still gonna have built-in support for custom callback lists, just gotta decide what the option will look like from now on
    Last edited by awsdert; 02-22-2023 at 01:40 PM. Reason: Left over note needed removing

  2. #2
    Registered User
    Join Date
    Feb 2023
    Posts
    12
    Quote Originally Posted by awsdert View Post
    both printf and scanf have both baggage and an inflexible design, I learned that the hard way when implementing my own specifiers etc, I've now decide to start converting my existing implementations in favour of something new, this is what I've decided so far:

    Code:
    /* Originally I wanted to mimic the existing printf/scanf standards, the
     * implementing of it enlightened me to how inflexible & inefficient the design
     * was so now I declaring a new standard and ignoring printf/scanf for now
     *
     * Arguments will be read like this: %/.../`options`'scalers'type%
     *
     * %% Will as usual mean just print/parse the character %
     *
     * The options get enclosed in a string so they cannot get confused with the
     * type specifier, the same reason applies to the scalers that get enclosed in
     * a string also. The /.../ means read a string in for regular expression
     * parsing and apply the options in the /.../ to the regular expression, the
     * options will be the same as in javascript, namely:
     *
    d Generate indices for substring matches.
    g Global search.
    i Case-insensitive search.
    m Allows ^ and $ to match newline characters.
    s Allows . to match newline characters.
    u "Unicode"; treat a pattern as a sequence of Unicode code points.
    y Perform a "sticky" search that matches starting at the current position in the target string.
     *
     * NOTE: Regular expression parsing is a PLANNED fetaure for the official
     * release of paw, for now it is currently unimplemented
     *
     * The last option of a kind takes precedence over previous options, for
     * example "%`<|>`s%" would right align the output string if padding is added
     *
     * Where * is the part of the option the * can be substituted for a literal,
     * for example %`c010`s% means to read the character and pad the output/input
     * if it is not at least 8 characters wide %`c*`s% carries same meaning but
     * instead of just 1 pawd for the character being read from the va_list 2 are
     * read, the 1st for the character, the 2nd for how many characters to pad it
     * to
     *
     * For strings paw supports the following:
     *
     * s    Raw string
     * sv    Buffer string
     *
     * 'l'    pawls/pawlsv
     * 't'    pawts/pawtsv
     * 'k'    pawks/pawksv
     * '8l'    paw8ls/paw8lsv
     * '16l'paw16ls/paw16lsv
     * '32l'paw32ls/paw32lsv
     *
     * `?`    Replace unsupported characters with this pawzc character
     * `<`    Left aligns output if padding is added, default
     * `|`    Center aligns output if padding is added, padding on either side will
     *     be equal even if it means more than the stated padding
     * `>`    Right aligns output if padding is added
     * `c*`    Read 2 values in, pad output/input with character c until it is at
     *    least * characters wide, the character is always read in as a pawd
     * `l*`    Read 2 values in, pad output/input with character c until it is at
     *    least * characters wide, the character is always read in as a pawzc
     * `^*` Repeat the output * times
     * `.*`    When printing this is the EXACT length of the string, pawlen etc are
     *    skipped in favour of this.
     *
     *    When parsing this is the max characters to fill the string with,
     *    default is 0, without defining this option your strings will always be
     *    empty unless it was into buffers that it can increase the size of
     * `#`    Prefix appropriatly, for example insert L" to the front if
     *    input/output is supposed to be pawls/pawlsv
     * '!'    Suffix appropriatly, for strings this just means append "
     *
     * For numbers the following is supported:
     *
     * b    Binary variant, default is pawu
     * d    Signed variant, default is pawd
     * u    Unsigned variant, default is pawu
     * o    Octal variant, default is pawu
     * x    Lowercase hexadecimal variant, default is pawu
     * x    Uppercase hexadecimal variant, default is pawu
     * p    Uppercase hexadecimal pointer, always void*, # is always on
     *
     * 'f'    pawfd
     * 'lf'    pawlfd
     * 'jf'    pawjfd
     * 'kf'    pawkfd
     * 'hh'    pawhhd/pawhhu
     * 'h'    pawhd/pawhu
     * 'l'    pawld/pawlu
     * 'll'    pawlld/pawllu
     * 'j'    pawjd/pawju
     * 'k'    pawkd/pawku
     * 'v'    pawvd/pawvu
     * '8'    paw8d/paw8u
     * ...    All the paw#d, paw#ld, paw#qd, paw#fd variants follow suit
     *
     * '+'    Same as standard printf definition, forces a sign for numbers
     * `?`    Replace unsupported digits with this pawzc character (L option only)
     * `<`    Left aligns output if padding is added, default
     * `|`    Center aligns output if padding is added, padding on either side will
     *     be equal even if it means more than the stated padding
     * `>`    Right aligns output if padding is added
     * `c*`    Read 2 values in, pad output/input with character c until it is at
     *    least * characters wide, the character is always read in as a pawd
     * `l*`    Read 2 values in, pad output/input with character c until it is at
     *    least * characters wide, the character is always read in as a pawzc
     * `^*` Repeat the output * times
     * `.*`    When printing this is the EXACT length of the string, pawlen etc are
     *    skipped in favour of this.
     *
     *    When parsing this is the max characters to fill the string with,
     *    default is 0, without defining this option your strings will always be
     *    empty unless it was into buffers that it can increase the size of
     * `#`    Prefix appropriatly, for example a hexadeximal value will have 0x
     *     prefixed, always active for pointers
     * '!'    Suffix appropriatly, for example a pawlu will have UL appended
     *
     * Planned to support on official release
     * `,`    Digits should be grouped into 1000s with locale defined separator,
     *    I don't have an implementation for this presently, so until I do there
     *    will be no beta of paw
     * `L`Use locales alternative digit representations, again I need to do some
     *    research and implement this before I declare paw in beta state
     *
     * */
    
    RAWPAW_API pawd pawio_outputv( PAWIO io, pawls args, va_list *va );
    RAWPAW_API pawd pawio_outputf( PAWIO io, pawls args, ... ) PAW_ATTR_HOT;
    
    RAWPAW_API pawd pawio_expectv( PAWIO io, pawls args, va_list *va );
    RAWPAW_API pawd pawio_expectf( PAWIO io, pawls args, ... ) PAW_ATTR_HOT;
    What other options do you think would be desired in a new alternative? I'm still gonna have built-in support for custom callback lists, just gotta decide what the option will look like from now on
    Part of solving a problem is recognizing there is one, and I have to ask what is the problem you see with the printf and scanf family of functions? I am the first to recognize that C has quite a bit of historical baggage, but that being said, I do still have to ask since it is rather difficult to gauge the quality of something without knowing what problems it was trying to solve. Basically, what I am asking, is what historical baggage do you see in the printf family?

  3. #3
    Registered User
    Join Date
    Sep 2020
    Posts
    425
    printf() and scanf() are what they are... a relatively flexible way to formatted output to or from a FILE *.

    IMO they really are Swiss Army Knife functions, and where they don't naturally fit should be replaced with something more tailored to your needs, that will also usually work better.

    Making a different or "bigger" Swiss Army Knife, with more features, more adapters and extra gizmos, that is even harder to use is the wrong answer. The correct answer it to make the single purpose tool that does just the job you need, and does it really well with minimal fuss.

    90% of people's needs that printf() doesn't meet will be pretty much unique to them, they haven't read the manual or possibly they are using it wrong.

  4. #4
    Registered User awsdert's Avatar
    Join Date
    Jan 2015
    Posts
    1,733
    Quote Originally Posted by interglobber View Post
    Part of solving a problem is recognizing there is one, and I have to ask what is the problem you see with the printf and scanf family of functions? I am the first to recognize that C has quite a bit of historical baggage, but that being said, I do still have to ask since it is rather difficult to gauge the quality of something without knowing what problems it was trying to solve. Basically, what I am asking, is what historical baggage do you see in the printf family?
    1st there's the specifier issue, it's easy for specifiers to get confused if they support modifiers &/or that are the same as other specifiers, by forcibly distinguishing between them I can support int32_t/int_least32_t etc with easily associated specifiers.

    2nd There's the issue of distinguishing when something is or is not a specifier when it's up against text that happens to be the same, let's say for instance one defined the specifier l for local timestamp, yet it is also a modifier for numbers & strings, if that timestamp happens to be followed by one of their specifiers it will be confused

    3rd There's also options that just don't make visual sense like the - option, without looking at the docs or having it in memory newbs could easily mistake it for serving the same purpose the + option which would just confuse them, because it's already been defined in the printf/scanf options it can't even be fixed for them, by going with my own standard I can more easily define options like this with more sensible characters before the 1st official release

    4th printf/scanf only support single character width specifiers, with this I can support longer ones for external libraries, like for example "std::string" can be defined as a specifier itself which in turn allows c++ to take advantage of the same function without making modifications to the source code of it

    I'm sure someone else can think of more problems with printf & co but that should be enough to indicate how poor the design was, it's a case of "great idea, poor execution", if I'm going to write a library that is meant to eventually compete with the standard library to some extent then I should make sure to learn from it's mistakes before making my 1st official release of paw, since abi is not a concern I'm even able to define an actual max size integer type (pawkd/pawku in this instance).

    I'm even adding support for kernel/host side defines for when a system wants to provide it's own equivalents, this will let driver developers seamlessly switch to kernel side equivalents without re-writing their own source code for them specifically, this would prove most useful if they're developing in user mode 1st to check for memory leaks before compiling the finished result for the kernel

  5. #5
    Registered User awsdert's Avatar
    Join Date
    Jan 2015
    Posts
    1,733
    Quote Originally Posted by hamster_nz View Post
    printf() and scanf() are what they are... a relatively flexible way to formatted output to or from a FILE *.

    IMO they really are Swiss Army Knife functions, and where they don't naturally fit should be replaced with something more tailored to your needs, that will also usually work better.

    Making a different or "bigger" Swiss Army Knife, with more features, more adapters and extra gizmos, that is even harder to use is the wrong answer. The correct answer it to make the single purpose tool that does just the job you need, and does it really well with minimal fuss.

    90% of people's needs that printf() doesn't meet will be pretty much unique to them, they haven't read the manual or possibly they are using it wrong.
    How's it harder to use? It's clearly defined where options start and end, clearly defined where modifiers start and end, scanf already partially supported regex with the option [...], I just made a proper place for a full regex instead, how it's interpreted will be left to a callback that can be defined by an option I'll later define when I get started on the expectf side of things where it actually gets used (outputf would just ignore the string provided after extract it from the va_list). Instead of using a global function a callback and specifier can just be defined as an argument before they're used. I honestly cannot see any point where this is harder to use than the standard it's aiming to be an alternative to

  6. #6
    Registered User awsdert's Avatar
    Join Date
    Jan 2015
    Posts
    1,733
    Quote Originally Posted by hamster_nz View Post
    printf() and scanf() are what they are... a relatively flexible way to formatted output to or from a FILE *.

    IMO they really are Swiss Army Knife functions, and where they don't naturally fit should be replaced with something more tailored to your needs, that will also usually work better.

    Making a different or "bigger" Swiss Army Knife, with more features, more adapters and extra gizmos, that is even harder to use is the wrong answer. The correct answer it to make the single purpose tool that does just the job you need, and does it really well with minimal fuss.

    90% of people's needs that printf() doesn't meet will be pretty much unique to them, they haven't read the manual or possibly they are using it wrong.
    Seriously, if you're gonna say it's harder to use then be explicit about HOW it is harder to use, nothing is improved if people just say something is wrong/hard/etc without also explaining why/how/etc

  7. #7
    Registered User awsdert's Avatar
    Join Date
    Jan 2015
    Posts
    1,733
    Okay, I did just think of a way to improve the usability of the functions, 1 common problem with printf/scanf is the mis-placing of the arguments that were specified, that's because of them all be specified in the one string, I'll make it so the arguments immediately follow, so for example: paws_outputf( dst, len, "%s", "Hello", " %s", "World!", NULL );

  8. #8
    Registered User
    Join Date
    Feb 2023
    Posts
    12
    I honestly have to agree with hamster_nz here. Especially with the possibility of adding a regular expression handler, which just seems unnecessary in my eyes. Regular expression engines are very complex beasts, not to mention how heavy they can be.

    That being said, I can get behind making code more explicit, so I would support something like using the > or < characters for padding something like zeros.

    4th printf/scanf only support single character width specifiers, with this I can support longer ones for external libraries, like for example "std::string" can be defined as a specifier itself which in turn allows c++ to take advantage of the same function without making modifications to the source code of it
    While I don't deny that there can be scenarios where this would be useful (you could think of scenarios where all of these would be useful), just because you can think of a scenario for it, does not mean it should be added! There is a fine line between providing useful functionality, and functionality that is rarely used. You don't need to cater to every possible formatting scenario, not only because that would be a lot of stuff for you to maintain, but also because it leads to feature creep which I don't think I need to explain the problems with.

    If you are going to re-write the printf interface (which, if I'm honest, I don't think there is much reason to do in the first place) if anything, I'd say drop the format string in its entirety. Rather than use a format string, write a string building library which allows you to compose together functions such as write_int, write_float, write_n_strings, etc. This:
    • Makes code more testable by separating it into distinct chunks
    • Allows the code to be used in scenarios *other* than strict output formatting without having to make an entirely new function for it
    • Allows arguably more flexible string formatting since you can more easily compose pieces together in arbitrary ways.


    Aside from writing insanely portable code, part of what draws me to C is the control the language gives you. Having a monolithic print function, in my opinion, makes code less flexible and gives you less control. The language's simplicity-- the ability to understand the function of code at a glance due to having less hidden control flow-- is what makes it great to me. Might be something to consider when writing your stuff.

  9. #9
    Registered User
    Join Date
    Sep 2020
    Posts
    425
    Quote Originally Posted by awsdert View Post
    Seriously, if you're gonna say it's harder to use then be explicit about HOW it is harder to use, nothing is improved if people just say something is wrong/hard/etc without also explaining why/how/etc
    I guess you haven't seen the stupid regular expression-like format strings that some people come up with?

    Just the first example I found on Stack Overflow:
    Code:
    scanf("%4[^,],%4[^,],%79[^,],%d", sem, type, title, &value)
    Can you tell me what's going on there, and how it will react to bad input?

    What happens if a line is missing a comma?

    What if somebody puts a comma in their data field for "title"?

    It's something that the programmer expected if the data is as expected, but otherwise is junk.

  10. #10
    Registered User awsdert's Avatar
    Join Date
    Jan 2015
    Posts
    1,733
    Quote Originally Posted by interglobber View Post
    I honestly have to agree with hamster_nz here. Especially with the possibility of adding a regular expression handler, which just seems unnecessary in my eyes. Regular expression engines are very complex beasts, not to mention how heavy they can be.

    That being said, I can get behind making code more explicit, so I would support something like using the > or < characters for padding something like zeros.



    While I don't deny that there can be scenarios where this would be useful (you could think of scenarios where all of these would be useful), just because you can think of a scenario for it, does not mean it should be added! There is a fine line between providing useful functionality, and functionality that is rarely used. You don't need to cater to every possible formatting scenario, not only because that would be a lot of stuff for you to maintain, but also because it leads to feature creep which I don't think I need to explain the problems with.

    If you are going to re-write the printf interface (which, if I'm honest, I don't think there is much reason to do in the first place) if anything, I'd say drop the format string in its entirety. Rather than use a format string, write a string building library which allows you to compose together functions such as write_int, write_float, write_n_strings, etc. This:
    • Makes code more testable by separating it into distinct chunks
    • Allows the code to be used in scenarios *other* than strict output formatting without having to make an entirely new function for it
    • Allows arguably more flexible string formatting since you can more easily compose pieces together in arbitrary ways.


    Aside from writing insanely portable code, part of what draws me to C is the control the language gives you. Having a monolithic print function, in my opinion, makes code less flexible and gives you less control. The language's simplicity-- the ability to understand the function of code at a glance due to having less hidden control flow-- is what makes it great to me. Might be something to consider when writing your stuff.
    I had planned on using callbacks anyways but my view had been narrow, you just enlightened me to a proper fix, I assume you meant something like this:

    Code:
    paws_outputf( dst, size, rdva_paws, "Hello ", rdva_paws, "World!", NULL );
    I VERY much like that idea so I'll go with it unless someone comes up with a better idea, now to read the next post

  11. #11
    Registered User awsdert's Avatar
    Join Date
    Jan 2015
    Posts
    1,733
    Quote Originally Posted by hamster_nz View Post
    I guess you haven't seen the stupid regular expression-like format strings that some people come up with?

    Just the first example I found on Stack Overflow:
    Code:
    scanf("%4[^,],%4[^,],%79[^,],%d", sem, type, title, &value)
    Can you tell me what's going on there, and how it will react to bad input?

    What happens if a line is missing a comma?

    What if somebody puts a comma in their data field for "title"?

    It's something that the programmer expected if the data is as expected, but otherwise is junk.
    I'm not saying regular expressions would be required or even advised but when done right they do a lot for you, in any case interglobber definitely enlightened me to a simpler way to support regex expressions without actually supporting them, unless you have something to say about that too?

  12. #12
    Registered User awsdert's Avatar
    Join Date
    Jan 2015
    Posts
    1,733
    Welp, this is my revised header code:

    Code:
    /* 1st call will ALWAYS pass NULL to all parameters, you are expected to
     * return the size of the object you need between calls
     *
     * 2nd call you are expected to consume your data from the va but not output
     * it (if an output callback), return the number of characters expected to be
     * printed on the 3rd call, this is added to a total so that a single
     * allocation can be made for any buffer attached to the IO's ud, tmp will be
     * cleaned after you return, until them you may use it as if it started as a
     * blank slate, expect your object in ud
     *
     * 3rd call expects you to print to tmp your string in wchar_t format, then
     * pass it to the io->cb_list->printCB callback. The io callbacks are
     * responsible for converting to the destination character encoding,
     * wide characters make it less likely a character will be unsupported
     * for printing to tmp, va WILL be NULL at this point
     *
     * , */
    typedef pawd (*pawio_va_cb)( pawio *io, pawmem *tmp, void *ud, va_list *va );
    
    /* Originally I wanted to mimic the existing printf/scanf standards, the
     * implementing of it enlightened me to how inflexible & inefficient the design
     * was so now I declaring a new standard and ignoring printf/scanf for now.
     *
     * The standard I WAS going to implement was definitely more efficient but
     * after some discussion on the forum I normally go to
     *
     * https://cboard.cprogramming.com/c-programming/181566-looking-thoughts-my-new-alternative-printf-scanf-post1307908.html#post1307908
     *
     * I found that there was yet a better way that was even more efficient than
     * what I had in mind, I'm now going down that route instead
     * */
    
    PAW_ATTR_HOT RAWPAW_API pawd pawio_outputv( const pawio io, pawmem *tmp, va_list *va );
    PAW_ATTR_HOT RAWPAW_API pawd pawio_outputf( const pawio io, pawmem *tmp, ... );
    
    PAW_ATTR_HOT RAWPAW_API pawd pawio_expectv( const pawio io, pawmem *tmp, va_list *va );
    PAW_ATTR_HOT RAWPAW_API pawd pawio_expectf( const pawio io, pawmem *tmp, ... );

  13. #13
    Registered User
    Join Date
    Feb 2023
    Posts
    12
    Quote Originally Posted by awsdert View Post
    I had planned on using callbacks anyways but my view had been narrow, you just enlightened me to a proper fix, I assume you meant something like this:

    Code:
    paws_outputf( dst, size, rdva_paws, "Hello ", rdva_paws, "World!", NULL );
    I VERY much like that idea so I'll go with it unless someone comes up with a better idea, now to read the next post
    I was thinking more like this:
    Code:
    int main() {
        char *number_to_pad = "99";
        char *buffer = malloc(128 + 1);
    
        /* Pad a string with (4 - strlen(number_to_pad)) zeros */
        write_n(&buffer, "0", 4 - strlen(number_to_pad);
        write_str(&buffer, number_to_pad);
    
        /* Write raw integer into the buffer */
        write_int(&buffer, 493);
    }
    This is type-safe, and in my opinion is much more flexible of a string formatting system than even printf. Although, I still like printf, but if you are going to redesign it, like I said, if anything you should drop the format string.

  14. #14
    Registered User awsdert's Avatar
    Join Date
    Jan 2015
    Posts
    1,733
    Quote Originally Posted by interglobber View Post
    I was thinking more like this:
    Code:
    int main() {
        char *number_to_pad = "99";
        char *buffer = malloc(128 + 1);
    
        /* Pad a string with (4 - strlen(number_to_pad)) zeros */
        write_n(&buffer, "0", 4 - strlen(number_to_pad);
        write_str(&buffer, number_to_pad);
    
        /* Write raw integer into the buffer */
        write_int(&buffer, 493);
    }
    This is type-safe, and in my opinion is much more flexible of a string formatting system than even printf. Although, I still like printf, but if you are going to redesign it, like I said, if anything you should drop the format string.
    That is doable with the callback I redesigned anyways, just pass NULL as the variable array and the matching ud object that is expected for it, just create a wrapper function that does that to achieve the same, for example:

    Code:
    typedef struct _wcs { int len; const wchar_t *txt; } wcs;
    
    pawd write_wcsCB( const pawio *io, pawtmp *tmp, void *ud, va_list *va )
    {
    	wcs *cs = ud;
    	if ( !io ) return sizeof(wcs);
    	if ( !va )
    	{
    		io->printCB( io->ud, cs->txt, cs->len );
    		pawdel( cs->txt );
    		return cs->len;
    	}
    	...
    }
    int write_wcs( const pawio *io, const wchar_t *wcs, size_t len )
    {
    	wcs cs = {len,pawgetUD( len )};
    	if ( !(cs.txt) ) return -1;
    	pawcpy( cs.txt, wcs, sizeof(wchar_t) * len );
    	return write_wcsCB( io, NULL, &cs, NULL );
    }

  15. #15
    Registered User awsdert's Avatar
    Join Date
    Jan 2015
    Posts
    1,733
    Incidentally for skipping character encoding conversion and just writing raw bytes it would be the io->writeCB( io->ud, list, sizeof(T) * count ); call, the purpose of the printCB call is to expect exactly 1 type, a wide character string, and convert the string to the expected output encoding, this method allows loading in only the character converter needed via a library instead of having all kinds in memory at once, the idea is that the constructor of the io object is responsible for making sure characters are printed correctly, I'm thinking of adding another callback or 2 to pass integers & floats to and just tell it the target size, then it can decide if it needs to switch the endian or not, at no point will the general callbacks using the io object need to know what the output/input encoding/endian is.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Alternative console output functions to printf()?
    By jafadmin in forum C Programming
    Replies: 17
    Last Post: 04-15-2014, 11:46 PM
  2. What is a good alternative for scanf?
    By Karyumi in forum C Programming
    Replies: 2
    Last Post: 03-06-2012, 11:37 AM
  3. Need help. printf() and scanf().
    By HBK in forum C Programming
    Replies: 19
    Last Post: 07-17-2011, 04:04 PM
  4. scanf and printf
    By gmanUK in forum C Programming
    Replies: 5
    Last Post: 11-25-2005, 03:03 PM
  5. Alternative forms of printf.
    By OOPboredom in forum C Programming
    Replies: 5
    Last Post: 05-07-2004, 01:21 PM

Tags for this Thread