1. 19
    1. 14

      Everybody’s a three star programmer, they just use structs to make it less confusing.

      foo->bar->baz->quux
      

      there you go, three stars right there, but it’s not confusing.

      Three star programmers are just beginners who didn’t learn how to use structs yet.

      1. 1

        that’s a great perspective, thanks for sharing. (c noob here)

      2. 5

        When C compilers more widely became capable of passing and returning structs by value, no-star programming became much easier.

        1. 4

          To reach the level of 2 star programmer, all you need is to write a C program with a main function, like this:

          int main(int argc, char** argv) { ... }
          

          Then, if you need to process your command line arguments, and deal for initial arguments beginning with '-', you might write:

             while (--argc > 0 && **++argv == '-') ...
          

          Oh, but what if we want to move the flag processing into a function?

             process_flags(&argc, &argv);
          

          and then the definition will be:

             void process_flags(int* argc, char*** argv) { ... }
          

          Three star!

          1. 4

            resubmitted today: last it was submitted 5 years ago, I am sure new members will surely enjoy reading this.

            1. 4

              Not to be confused with Three Star Programmer, a (likely) Turing-complete esoteric language whose only instruction is, in C syntax:

              (***x)++
              
                1. 1

                  A couple comments hint at this, but none says it very explicitly. I would write this function with a struct as an out param:

                  https://github.com/spc476/mod_blog/blob/8eb3f660edd5a35c09616d058307e5a166052c99/src/blog.c#L335

                  static size_t blog_meta_read(
                          char             ***plines,
                          char       const   *name,
                          struct btm const   *date
                  )
                  

                  would turn into

                  struct lines_t {
                    char** plines;
                    size_t i;
                  };
                  
                  static void blog_meta_read(
                    char const *name,
                    struct bm const *date,
                    struct lines_t* out) {
                  ...
                    out->plines = lines;
                    out->i = i;
                  ...
                  }
                  
                  

                  I like this style better because it clearly separates inputs and outputs. Right now there’s a both a return value and an out param.

                  At the call site that would be:

                  struct line_t lines = {0};
                  blog_meta_read("foo", mydate, &lines);
                  
                  

                  So yeah I think you never need three stars. I find it very confusing!

                  1. 2

                    Since I wrote that, I did change the function:

                    static size_t blog_meta_read(
                            char             **plines[],
                            char       const  *name,
                            struct btm const  *date
                    )
                    

                    which gets rid of one star. I’m not sure if I would go with your solution. It adds yet another abstraction (a structure) and I kind of like the look of the call sites now:

                    char   **authors;
                    char   **class;
                    char   **status;
                    char   **titles;
                    char   **adtag;
                    size_t   numa;
                    size_t   numc;
                    size_t   nums;
                    size_t   numt;
                    size_t   numad;
                    size_t   maxnum;
                    
                    numa   = blog_meta_read(&authors,"authors",&entry->when);
                    numc   = blog_meta_read(&class,  "class",  &entry->when);
                    nums   = blog_meta_read(&status, "status", &entry->when);
                    numt   = blog_meta_read(&titles, "titles", &entry->when);
                    numad  = blog_meta_read(&adtag,  "adtag",  &entry->when);
                    maxnum = max(numa,max(numc,max(nums,numt)));
                    

                    That call to max() would get ugly with all the structure references. Further more, the call site was reworked to that form in November of 2011. I’m not sure what that says about the code …

                2. 3

                  When I was in school I wrote a program with a void***** once. I was tired, and code maintenance wasn’t really a thing I knew about yet.

                  1. 1

                    I’ve never actually seen three stars in practice. I sorta get ***argv example but I think there are other ways to write it, so nobody does that.

                    I’d be curious to see if anyone can point to a “justified” use of three stars in some real code. But honestly I think you can always rewrite it another way.

                    I’m usually a 1 star programmer, but recently I wrote some 2 star code which is kinda interesting:

                    static inline int J8EncodeOne(unsigned char** p_in, unsigned char** p_out,
                                                  int j8_escape) {
                    
                    

                    https://github.com/oilshell/oil/blob/master/data_lang/j8.h (some comments are outdated, should fix that)

                    This is for C-escaping strings. On each call,

                    • The input can move forward by 1 to 4 bytes because it also decodes UTF-8
                    • The output can move forward by 1 to 7 bytes, the max escape being either \uffff or \u{1f}, plus NUL terminator

                    The reason it’s like this is because this code is meant to be reusable from BOTH C and C++. We have callers that use malloc() (slow!), callers that use our GC allocator, and a C++ std::string caller just for fun.

                    If it didn’t have to be decoupled from allocation, then I would pass a mutable std::string that you append to.

                    You can write this in other ways, but I think if both pointers change a variable amount at each step, AND you don’t want any allocation mixed with the logic, it’s the easiest and clearest way to write it, as long as you have a good test suite.


                    Now this pointer arithmetic is wildly unsafe – I wouldn’t have done it years ago. But this function has little control flow and ASAN hits all the bugs. You can get 100% coverage.

                    ASAN caught the bug where the max output is 7 and not 6, because sprintf() writes a NUL terminator too. (Not technically needed here, but it’s convenient to use sprintf for hex escapes).

                  Stories with similar links:

                  1. Three Star Programmer via calvin 6 years ago | 25 points | 14 comments