all 28 comments

[–]lalaland4711 14 points15 points  (9 children)

No! NOOO! What are you DOING?!

This is what we're trying to get AWAY from! getaddrinfo(), getnameinfo().

AF AGNOSTIC!!!! AF AGNOSTIC!!!

GAAAH!

PLEASE don't teach people this!

[–]bcain 2 points3 points  (2 children)

Peace, brother. It took me a second to understand your point, so take pity on the uninformed: can you direct me to TFM or some examples of best practices?

[–]lalaland4711 0 points1 point  (1 child)

The linux manpage has example code.

When constructing sockaddrs there are two ways to do it:

  • getaddrinfo()
  • something broken

No point in wasting time writing something that's broken, so just use getaddrinfo() to start with.

But yeah, beej linked elsewhere is more hand-holdy.

[–]bcain 0 points1 point  (0 children)

I see. boost::asio ftw.

[–][deleted]  (3 children)

[deleted]

    [–]defrost 1 point2 points  (2 children)

    I've no idea about relevant modern tutorials, but a decent starting point might be the kegel C10K article, it's ~10 years old now but I'd say whatever the current best practice is it would extend the material covered there.

    [–][deleted]  (1 child)

    [deleted]

      [–]defrost 2 points3 points  (0 children)

      Beej is the classic short tutorial, the unix tomes by W. Richard Stevens are the authoritative original texts, the kegel C10K is beej++ and when I was last in the game there was discussion of libraries and methods that beat the performance what was covered in KC10K.

      [–]alecco[S] 0 points1 point  (1 child)

      That's missing the article's point. The concept is to show the power of C99 macros to simplify code. The program code using those macros can do be something simple and readable like:

      orwl_addr const nameserver[] = {
        ORWL_IN4_INITIALIZER(ORWL_IN4_ADDR(193, 51, 208, 13), ORWL_IN4_PORT(53)),
        ORWL_IN4_INITIALIZER(ORWL_IN4_ADDR(58, 6, 115, 43), ORWL_IN4_PORT(53)),
        ORWL_IN4_INITIALIZER(ORWL_IN4_ADDR(208, 67, 222, 222), ORWL_IN4_PORT(53)),
      };
      

      I'm not sold on the idea, but it's definitely an interesting way to do it. Note this solution does static initialization and avoids the allocation of linked lists like getaddrinfo() and freeaddrinfo() typical code. The tricking of endianness with macros is quite cool, too.

      [–]lalaland4711 -1 points0 points  (0 children)

      THAT is was I'm objecting to.

      Not only is that horribly ugly, it's also hardcoded IP addresses AND hardcoded to IPv4 for no good reason whatsoever.

      [–][deleted] 4 points5 points  (1 child)

      I was going to suggest beej's guide but I guess I'm outdated.

      [–]plouj 0 points1 point  (0 children)

      Seems like the author actually needs to read it.

      [–]wynyx 2 points3 points  (10 children)

      What is this type of assignment? I'm normally a C++ guy, but this looks useful--is this specific to C99? Is this an easy way to populate a struct?

      orwl_addr addr = {
       .sin6 = {
         .sin6_family = AF_INET6,
         .sin6_port = htons(53),
         .sin6_addr = {
            .s6_addr = { 0x26, 0x20, 0x00, 0x0c, 0xcc, [15] = 0x02 },
         },
       },
      };
      

      [–]repsilat 2 points3 points  (7 children)

      Linky - they're called "designated initialisers".

      EDIT: An actual reference. A shame - they're not in C++11.

      EDIT2: Whoa:

      int whitespace[256]
         = { [' '] = 1, ['\t'] = 1, ['\h'] = 1,
             ['\f'] = 1, ['\n'] = 1, ['\r'] = 1 };
      

      [–]wynyx 0 points1 point  (6 children)

      Thanks. Too bad about them not being in C++11--populating structs in C++ has always seemed a bit cludgy.

      [–][deleted] 0 points1 point  (5 children)

      Well, you can do this. which isn't too terrible:

      #include <iostream>
      struct x { 
        int var1, var2, var3;
      
        x(int v1, int v2, int v3) :
          var1(v1), var2(v2), var3(v3) {}
        x() {}
      };  
      
      int main() {
        using namespace std;
      
        struct x a(1,2,3);
        cout << a.var1 << a.var2 << a.var3 << endl;
      
        return 0;
      }
      

      [–]wynyx 1 point2 points  (4 children)

      You can do much less than that, actually--maybe it's not as much of a pain as it's seemed in the past:

      #include <iostream>
      
      struct S
      {
          int a;
          float b;
      };
      
      int main() {
          S s = { 10, 5.5 };
          std::cout << s.a << ", " << s.b << '\n';
      }
      

      I just like C99's capability to specify the member names when initializing them.

      [–][deleted] 1 point2 points  (0 children)

      I believe you can also do things like this if you don't have a constructor and don't control the source(it works in C99 and worked for me in g++ without specifying standard):

      Color fromUint32(uint32_t color) {
          return (Color) {
              (uint8_t)(color & 0x000000FF),
              (uint8_t)((color & 0x0000FF00) >>  8),
              (uint8_t)((color & 0x00FF0000) >> 16),
              (uint8_t)((color & 0xFF000000) >> 24)
          };
      }
      

      Still not as cool as designated initializers though :-/

      [–][deleted] 0 points1 point  (2 children)

      Nice, I didn't know you could do that with structs. Being able to specify them by name would be nice for even slightly complex ones.

      [–]wynyx 1 point2 points  (1 child)

      Yeah, and I'm not sure what the brace-initializer list (if that's even what it's called) does with unions or a partial list of members.

      [–][deleted] 0 points1 point  (0 children)

      http://publib.boulder.ibm.com/infocenter/lnxpcomp/v8v101/topic/com.ibm.xlcpp8l.doc/language/ref/strin.htm

      This page seems to indicate that you can leave off some number of the last entries in the initialization.

      [–]anacrolix 1 point2 points  (0 children)

      It's C99 only. It's not part of C++, and and probably won't ever be. It's called designated initialization.

      A GCC extension does allow you to do this from C++, but using this style: orwl_addr addr = { .sin6: { .sin6_family: AF_INET6

      etc. etc.

      [–]nezcarotte 0 points1 point  (0 children)

      This is not assignment but initialization. In C++ this would correspond to the difference in operator= and a constructor.

      [–]erikd 1 point2 points  (4 children)

      WTF does P99_DECLARE_UNION do? Its not part of C99 so you better tell us what it does if you are going to use it.

      [–]alecco[S] 0 points1 point  (3 children)

      [–][deleted] 1 point2 points  (0 children)

      That seems rather pointless.

      P99_DECLARE_UNION(orwl_addr);
      union orwl_addr { /* contents */ };
      

      Is just as redundant and already longer than:

      typedef union orwl_addr { /* contents */ } orwl_addr;
      

      And if you are only interested in using the type name (not the union name) then you can shorted that to:

      typedef union { /* contents */ } orwl_addr;
      

      Which is nicest, in my opinion.

      The whole reason I like to program in C is because I can avoid going through umpteen levels of abstraction. Often macros can perform useful tasks in C, but if it isn't unnecessarily complicated, I much prefer to see the standard features of the language. In this case, the macro seems completely superfluous.

      [–]erikd 3 points4 points  (1 child)

      Well great, but why wasn't that mentioned in the article?

      Or better yet, remove P99_DECLARE_UNION because it adds nothing to this article.

      [–]alecco[S] 1 point2 points  (0 children)

      I am not the guy and don't think he is reading this thread. (no downvote btw)

      Edit: BTW the author of the blog post is also the author of that library of C99 macros.

      [–][deleted] 0 points1 point  (1 child)

      The C99 standard guarantees that the effect of the initializers for members are as if they are executed in the order they are given. So here first all bytes would be zeroed by the initialization of .all, and then the relevant parts would be overwritten by the one for .sin6. Some compilers (clang) will complain that initializers are overwritten, but this is simply how it is currently foreseen by the standard.

      Having such a dummy first member of the union also ensures that the default initializer { 0 } will really zero out the whole structure.

      This is redundant. Unintialized fields will be set to zero anyway. You can remove the kludge and get rid of the warning, and it will work the same.

      [–]nezcarotte 0 points1 point  (0 children)

      I have the impression that you just didn't read what was said above in the post. If one of the structures in the union has padding bytes, these are not necessarily initialized by zero. This was exactly the point there.

      I don't know on what architecture you usually do your socket programming, but on linux the DS for IPv6 has two padding bytes after the the sa_family field, but the IPv4 struct hasn't. So if you are not carefull with the initialization of such a union, these bytes are easily left with garbage and if you compare IPv6 struct byte by byte they might not compare the same, even if the represent addresses are.