Giter Club home page Giter Club logo

Comments (4)

mpaland avatar mpaland commented on June 4, 2024 1

Merged!
I renamed param 'user' to 'arg' for more common nomenclature.

from printf.

vgrudenic avatar vgrudenic commented on June 4, 2024 1

Hi guys! I have an additional question (suggestion) regarding @sgoll's merge.

The starting reason why I came to this is that on ARM GCC with -Wcast-align (which is one of the additional warnings parameters we use with ARM to avoid alignment issues) we get an alignment warning when casting the char* to a struct pointer in this function:

static inline void _out_fct(char character, char* buffer, size_t idx, size_t maxlen)
{
  // warning: cast from char* to out_fct_wrap_type* increases
  //          required alignment of target type
  
  ((out_fct_wrap_type*)buffer)->fct(character, ((out_fct_wrap_type*)buffer)->arg);
}

Although it isn't the case here (buffer always points to an actual struct instance when this function is called), the warning would generally make sense according to the C standard (6.3.2.3 ยง7):

If the resulting pointer is not correctly aligned for the referenced type, the behavior is undefined.

So the cast itself is not undefined behavior, unless the pointer is misaligned at runtime, which it isn't.

But anyway, at first I thought of using a #pragma to disable the warning, but then I also noticed that (as of this revision) the buffer parameter is actually a "special case", used only inside _out_buffer and it seems like there is no need to use it everywhere.

That is, writing to char* buffer is actually just a special case of the function call where the newly introduced void* arg would point to char* buffer, and the _out_buffer function would just cast it to char*, while _out_char would again simply ignore the arg part completely.

To clarify, this would require replacing the char* pointer with void*:

// char* buffer becomes void* arg, because this will work the same way for fctprintf    
typedef void (*out_fct_type)(char character, void* arg, size_t idx, size_t maxlen);

// out_fct_wrap_type takes this same signature
typedef struct {
  out_fct_type fct;
  void* arg;
} out_fct_wrap_type;

And then all calls to out(...) would be changed from

static int _vsnprintf(out_fct_type out, char* buffer, ...
{
  ...
  out(character, buffer, idx++, maxlen);
  ...
}

to

// no need to pass `out_fct_type` and `char*` separately to this function,
// because `out_fct_wrap_type` wraps them both
static int _vsnprintf(out_fct_wrap_type out, ...
{
  ...
  out.fct(character, out.arg, idx++, maxlen);
  ...
}

And then individual "overloads" of printf would change from:

int printf(const char* format, ...)
{
  ...      
  char buffer[1];
  const int ret = _vsnprintf(_out_char, buffer, (size_t)-1, format, va);
  ...
}

to something like this:

int printf(const char* format, ...)
{
  ...
  const out_fct_wrap_type out_fct_wrap = { _out_char, NULL };
  const int ret = _vsnprintf(out_fct_wrap, (size_t)-1, format, va);
  ...
}

Does this make sense? There would be no changes to the public interface, except the func signature in fctprintf (which perhaps isn't a bad thing because it lets the caller get the idx too).

Sorry for potential typos. If you are interested and don't think that this will change the code too much (shouldn't change the public interface though), I can make a PR.

Thanks!

from printf.

mpaland avatar mpaland commented on June 4, 2024

You are absolutely right! I'm a big fan of void* arguments passed to callback functions.
In the case above, I just thought it won't be necessary, but your example is a use case.
So, I merge your PR shortly. THANKS!

from printf.

mpaland avatar mpaland commented on June 4, 2024

Hello Vedran, thanks A LOT for your detailed description and bringing -Wcast-align to attention.
You got a point here, obviously the char* pointer is always correct aligned when pointing to the wrapper struct, but I agree, it's not 100% clean regarding the standard.

Suppressing warnings may help in certain situations but is not the intention here, because this module should work rocksolid in any environment.

I think, you are right, replacing the char* pointer by a void* might solve the problem. Going to investigate this...

from printf.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.