0

I have a project of recreating printf in my school.

I don't want to check the format specifiers for characters all the time in my code, as I find it messy, and ugly.

As for now, I have found a way by creating some global constant array, and checking using those. But I don't like the idea of having so many global variables in my code as well.

Is it one of those cases were global variables are ok? Or should I use another method to get what I want?

Here is how I'm starting:

Global const array

const char  g_sp_integer[] = {
    'd',    //signed decimal int
    'i',    //signed decimal int
    'o',    //unsigned octal
    'u',    //unsigned decimal int
    'x',    //unsigned hex int
    'X',    //unsigned hex int (uppercase)
    '\0'
};

My header

#ifndef  FT_PRINTF_H
# define  FT_PRINTF_H

# include <stdarg.h>
# include <stdint.h>
# include <stdlib.h>

# include "libft.h"

# define SUCCESS (int32_t)0
# define FAILURE (int32_t)-1

/*
** Those extern definitions are used to check the specifier flags
*/

extern const char   *g_sp_integer;

int ft_printf(const char *format, ...);

#endif

And my printf function

#include "ft_printf.h"

static int32_t  is_sp_integer(char c)
{
    uint32_t    i;

    while (g_sp_integer[i] != '\0')
    {
        if (g_sp_integer[i] == c)
            return (i);
        ++i;
    }
    return (FAILURE);
}

int ft_printf(const char *format, ...)
{
    va_list ap;
    char    *tmp;
    int32_t sp_type;

    tmp = format;
    va_start(ap, format);
    while (tmp != '\0')
    {
        if (tmp != '%')
        {
            ft_putchar(tmp);
            continue;
        }
        if ((sp_type = is_sp_integer(++tmp)) != FAILURE)
            ;   //parse_flag(sp_type);
        //continue checking the type of the specifier
    }
    va_end(ap);
    return (SUCCESS);
}

What I want to avoid:

Those are just bare-bones prototype, but I would like to know if there is a proper way to make my function as clean as that. Which means, in my opinion, that I'd like to avoid doing the check like this, if possible:

if (c == 'd' || c == 'i')
    //manage the integer flag
else if (c == 'o')
    //manage the octal flag, etc.

If it not possible and the best way is the one I'd like to avoid, please, let me know!

Thank you all for your patience, as finding good practices can sometimes be hard!

EDIT:

Solution I used:

While the first solution has the global answer to what I should have done in this case (which is using a static variable in that file), I have ended doing what was suggested in the second answer, as it fits my needs, and avoid using static or global variables.

Here is my function's code:

static int32_t is_sp_integer(char c) {
  const char    *sp_integer;
  const char    *sp_ptr;

  sp_integer = "dDioOuUxX";
  sp_ptr = sp_integer;
  while (*sp_ptr != '\0')
  {
    if (*sp_ptr == c)
      return (sp_ptr - sp_integer);
    ++sp_ptr;
  }
  return (FAILURE);
}

Thanks to all of you!

chbp
  • 301
  • 2
  • 10
  • 1
    how about this: `strchr(c, "diouxX") != 0`; also there is a set of standard functions in string.h, i.e. `isdigit` which you can use instead of is_sp_integer. – Serge Dec 12 '17 at 18:14
  • I cannot really use standard libraries apart from malloc, free, exit and write. But sure, I can code it and do that. Thanks for this idea. – chbp Dec 12 '17 at 18:15
  • @Serge Note concerning `strchr(c, "diouxX") != 0`, that when `c==0`, `strchr(c, "diouxX")` returns a non-NULL pointer. Should not be a problem to prevent `c== 0`. – chux - Reinstate Monica Dec 12 '17 at 19:17
  • @cbaillat [How to check that two format strings are compatible?](https://stackoverflow.com/q/28947528/2410359) goes through many of the same things. – chux - Reinstate Monica Dec 12 '17 at 19:20
  • `const char g_sp_integer[]` --> `static const char g_sp_integer[]`, now `g_sp_integer[]` only has file scope. – chux - Reinstate Monica Dec 12 '17 at 19:24

2 Answers2

1

g_sp_integer is only used inside the is_sp_integer function, so define it there:

static int32_t  is_sp_integer(char c)
{
    const char  g_sp_integer[] = {
        'd',    //signed decimal int
        'i',    //signed decimal int
        'o',    //unsigned octal
        'u',    //unsigned decimal int
        'x',    //unsigned hex int
        'X',    //unsigned hex int (uppercase)
        '\0'
    };

    uint32_t    i;

    while (g_sp_integer[i] != '\0')
    {
        if (g_sp_integer[i] == c)
            return (i);
        ++i;
    }
    return (FAILURE);
}
dbush
  • 205,898
  • 23
  • 218
  • 273
  • It is only used there as for now but i I will probably need to do multiple checks, as I will need to parse the tags, etc. Thanks for the suggestion though, maybe I'll try to do it that way. Thanks! – chbp Dec 12 '17 at 18:25
  • If you need to use it in multiple functions, define it at file scope with `static` and remove the `extern` declaration from the header, that way it's not visible in other source files. – dbush Dec 12 '17 at 18:26
  • Thanks, I will do that! – chbp Dec 14 '17 at 08:45
0

Here is a bit different implementation utilizing pointers. I think that it is a bit more more efficient than with arrays.

const char  *g_sp_integer= "diouxX";
static int32_t  is_sp_integer(char c)
{
    const char *p = g_sp_integer;
    while (*p) {
        if (*p == c)
            return (p - g_sp_integer);
        p++;
    }
    return (FAILURE);
}
Serge
  • 11,616
  • 3
  • 18
  • 28