2

The code comes from here

Given that in C++ you can use C libraries would you say that the following code is a legitimate C++ code? If not what changes need to be applied?

This code compiles with g++ and runs as expected.

UPDATE: Thank you for all answers. I'm still slightly confused as there's no agreement on whether or not this code comply with C++ standards. Will ask another question to dispel my doubts

UPDATE2: To moderators who closed this question: Just noticed that the question has been closed which I think is ridiculous. It's a down-to-earth technical question and I received down-to-earth technical answers.In case you haven't read fully the whole thread, here's the visual representation of the conclusion we've agreed on:

alt text

Clearly C++ is not a superset of C.

Closing questions that deal with coding standards is just wrong.

Client:

    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <errno.h>
    #include <string.h>
    #include <netdb.h>
    #include <sys/types.h>
    #include <netinet/in.h>
    #include <sys/socket.h>

    #include <arpa/inet.h>
    #define PORT "3490" // the port client will be connecting to 
    #define MAXDATASIZE 100 // max number of bytes we can get at once 

    // get sockaddr, IPv4 or IPv6:
    void *get_in_addr(struct sockaddr *sa){
        if (sa->sa_family == AF_INET) {
            return &(((struct sockaddr_in*)sa)->sin_addr);
        }

        return &(((struct sockaddr_in6*)sa)->sin6_addr);
    }

    int main(int argc, char *argv[]){
        int sockfd, numbytes;  
        char buf[MAXDATASIZE];
        struct addrinfo hints, *servinfo, *p;
        int rv;
        char s[INET6_ADDRSTRLEN];

        if (argc != 2) {
            fprintf(stderr,"usage: client hostname\n");
            exit(1);
        }

        memset(&hints, 0, sizeof hints);
        hints.ai_family = AF_UNSPEC;
        hints.ai_socktype = SOCK_STREAM;

        if ((rv = getaddrinfo(argv[1], PORT, &hints, &servinfo)) != 0) {
            fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
            return 1;
        }

        // loop through all the results and connect to the first we can
        for(p = servinfo; p != NULL; p = p->ai_next) {
            if ((sockfd = socket(p->ai_family, p->ai_socktype,
                    p->ai_protocol)) == -1) {
                perror("client: socket");
                continue;
            }

            if (connect(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
                close(sockfd);
                perror("client: connect");
                continue;
            }

            break;
        }

        if (p == NULL) {
            fprintf(stderr, "client: failed to connect\n");
            return 2;
        }

        inet_ntop(p->ai_family, get_in_addr((struct sockaddr *)p->ai_addr),
                s, sizeof s);
        printf("client: connecting to %s\n", s);

        freeaddrinfo(servinfo); // all done with this structure

        if ((numbytes = recv(sockfd, buf, MAXDATASIZE-1, 0)) == -1) {
            perror("recv");
            exit(1);
        }

        buf[numbytes] = '\0';
        printf("client: received '%s'\n",buf);
        close(sockfd);

        return 0;
    }

Server:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <signal.h>

#define PORT "3490"  // the port users will be connecting to
#define BACKLOG 10     // how many pending connections queue will hold

void sigchld_handler(int s){
    while(waitpid(-1, NULL, WNOHANG) > 0);
}

// get sockaddr, IPv4 or IPv6:
void *get_in_addr(struct sockaddr *sa){
    if (sa->sa_family == AF_INET) {
        return &(((struct sockaddr_in*)sa)->sin_addr);
    }

    return &(((struct sockaddr_in6*)sa)->sin6_addr);
}

int main(void){
    int sockfd, new_fd;  // listen on sock_fd, new connection on new_fd
    struct addrinfo hints, *servinfo, *p;
    struct sockaddr_storage their_addr; // connector's address information
    socklen_t sin_size;
    struct sigaction sa;
    int yes=1;
    char s[INET6_ADDRSTRLEN];
    int rv;

    memset(&hints, 0, sizeof hints);
    hints.ai_family = AF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_flags = AI_PASSIVE; // use my IP

    if ((rv = getaddrinfo(NULL, PORT, &hints, &servinfo)) != 0) {
        fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
        return 1;
    }

    // loop through all the results and bind to the first we can
    for(p = servinfo; p != NULL; p = p->ai_next) {
        if ((sockfd = socket(p->ai_family, p->ai_socktype,
                p->ai_protocol)) == -1) {
            perror("server: socket");
            continue;
        }

        if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes,
                sizeof(int)) == -1) {
            perror("setsockopt");
            exit(1);
        }

        if (bind(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
            close(sockfd);
            perror("server: bind");
            continue;
        }

        break;
    }

    if (p == NULL)  {
        fprintf(stderr, "server: failed to bind\n");
        return 2;
    }

    freeaddrinfo(servinfo); // all done with this structure

    if (listen(sockfd, BACKLOG) == -1) {
        perror("listen");
        exit(1);
    }

    sa.sa_handler = sigchld_handler; // reap all dead processes
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = SA_RESTART;
    if (sigaction(SIGCHLD, &sa, NULL) == -1) {
        perror("sigaction");
        exit(1);
    }

    printf("server: waiting for connections...\n");

    while(1) {  // main accept() loop
        sin_size = sizeof their_addr;
        new_fd = accept(sockfd, (struct sockaddr *)&their_addr, &sin_size);
        if (new_fd == -1) {
            perror("accept");
            continue;
        }

        inet_ntop(their_addr.ss_family,
            get_in_addr((struct sockaddr *)&their_addr),
            s, sizeof s);
        printf("server: got connection from %s\n", s);

        if (!fork()) { // this is the child process
            close(sockfd); // child doesn't need the listener
            if (send(new_fd, "Hello, world!", 13, 0) == -1)
                perror("send");
            close(new_fd);
            exit(0);
        }
        close(new_fd);  // parent doesn't need this
    }

    return 0;
}
Roman C
  • 49,761
  • 33
  • 66
  • 176
matcheek
  • 4,887
  • 9
  • 42
  • 73
  • 1
    What are you figure "legitimate" means in this context, exactly? – Karl Knechtel Dec 06 '10 at 23:36
  • Do you want to know if its stealing information or something? – blindstuff Dec 06 '10 at 23:38
  • 1
    Its C code. C++ uses a different style. But you often find C code burried inside C++ applications so its not a big deal. – Martin York Dec 06 '10 at 23:38
  • @Karl Knechtel: comply with C++ standards – matcheek Dec 06 '10 at 23:39
  • @blindstuff: forgive me words I'm choosing to express myself. I meant : does it apply to C++ standards – matcheek Dec 06 '10 at 23:42
  • In the context you provided it, btw, the reason you got downvoted could be that the question was for something to help learn how to *write* socket code in C++. This helps with learning a BSD-style sockets API, but doesn't help with writing C++ because (considered as C++) it's such questionable style. If the request wasn't for learning purposes, but someone needed code for a simple server that they could copy and paste into their C++ program, this would do the job. As suggested by the answers here, the comment "That is most definitely not C++" is exaggeration, more like "that is 'orrible C++". – Steve Jessop Dec 07 '10 at 00:52
  • @Steve Jessop: Thanks. I'd say I asked for my own benefit – matcheek Dec 07 '10 at 00:56

9 Answers9

3

From your comments it seems like you're really interested in how portable the code is, or "how can you be sure that it will compile on any C++ compiler not just g++".

Since each compiler has s different set of extensions they support (or even a different level of how well they support the standard, or which standard they support), one way is to try compiling it with different compilers.

If you can't do that, you can specify exactly which set of extenstions and/or standards that gcc will use to compile. For example:

--std=c89
--std=c90
--std=c99
--std=gnu90      (default for C compiles)
--std=gnu99

--std=c++98
--std=c++0x
--std=gnu++98    (default for C++ compiles)
--std=gnu++0x

There are probably other, see the gcc docs for more detail: http://gcc.gnu.org/onlinedocs/gcc/Standards.html

Also, the --pedantic option can make gcc much more strict about standards compliance.

Michael Burr
  • 333,147
  • 50
  • 533
  • 760
2

Personally I see C++ are more declarative than C which is highly procedural (though not as declarative as things like prolog).

server

ServerAction   action;
Server         server(action);

EventLoop      loop(server);
loop.processes();

Client

Address        addr("123.45.67");
Connection     connect(addr);

Message        message(Connection::Type::HTTP, "Plop");
connection.Send(message);
Martin York
  • 257,169
  • 86
  • 333
  • 562
2

This does comply with C++ standards, and in that sense it is legally C++.

However, there is nothing here that is C++ specific. You could have just as easily compiled this with gcc.

I think what others are noting here is the fact that this code is written in a procedural way, not in an object-oriented way. Most people associate C++ with object-oriented programming, and most modern books that discuss morally responsible C++ programming would say this is a bad way to write an app. In other words, it is very much not in vogue to write programs like this when an object-oriented paradigm is available to write this sort of application in.

This application is very difficult to maintain, and is written almost haphazardly. A good C programmer would split this problem into multiple functions instead of dumping most of it in main(). It works as an example of how to do client-server, but I would hesitate to write new code like this.

J. Polfer
  • 12,251
  • 10
  • 54
  • 83
  • 2
    There's far more to the difference between idiomatic C++ and idiomatic C than "object-oriented vs. procedural". In fact, many very talented C++ users would strongly object to the characterization of C++ as an exclusively, or even primarily "object-oriented" language. – Karl Knechtel Dec 07 '10 at 06:25
1

You’re not writing C++, you’re taking advantage of the fact that conforming, clean C code will compile as C++ with no problems. There are some places where C++ diverges from C, but the compiler will yell at you in those cases. You could use this as a base on top of which to write some effective C++; otherwise, there’s nothing special going on.

Josh Lee
  • 171,072
  • 38
  • 269
  • 275
1

This is valid, but not in the C++ preferred style.

For example, all the standard C libraries should be included <cstdio>, not <stdio.h>.

Another example, this code uses a #define when it could have used a constant integer.

Greg Hewgill
  • 951,095
  • 183
  • 1,149
  • 1,285
  • valid but not preferred, that means it is a C++ code. – matcheek Dec 06 '10 at 23:50
  • 2
    @matcheek: Valid syntactically. Not valid stylistically. If I asked you to write a C++ program for an interview question and you came back with this I would not trust you to be able to write modern C++ code and thus you would not get the job (as I need you to be able to work with others who understand the proper way to use the language). If I asked you to write a C program this would be a perfectly valid approach. – Martin York Dec 06 '10 at 23:57
1

Using C++, you could use better type checking features to avoid things like void*, #define, etc.

But since it does not use exclusive C features, it compiles with g++.

Julio Guerra
  • 5,523
  • 9
  • 51
  • 75
1

If I were to visit you at Scotland and speak Scottish with my Portuguese accent and (short) English vocabulary ... would you say I was speaking Scottish?

Assume everybody understood me and I understood everybody :-)

pmg
  • 106,608
  • 13
  • 126
  • 198
1

It is 'C' code that also conforms to the compatible subset within C++.

If it compiles it's legitimate, absent compiler bugs, but it seems to me that you have a different meaning for 'legitimate'.

user207421
  • 305,947
  • 44
  • 307
  • 483
0

I don't quite understand the question. I think you're asking whether you should just keep using it without any changes, and not worry about it - in which case I'd say yes. C++ is a superset of C - any valid C code is valid C++ code, as you see.

By convention, the standard headers have a new format -

#include <string.h>

becomes

#include <cstring>

but this isn't strictly necessary. You can't do that with the unistd and sys/ headers, anyway.

So if you're asking whether you can safely keep using it - go for it, but you may want to change errno.h to cerrno, stdio.h to cstdio, and so on.

Robert
  • 6,412
  • 3
  • 24
  • 26
  • I think I've got the answer. What I meant was:how can you be sure that it will compile on any C++ compiler not just g++. – matcheek Dec 06 '10 at 23:45
  • @matcheek: well, to be pedantic, in full generality you can't check it any way other than trying it. There is no formal way to check that a compiler follows a standard correctly so you'll just have to *suck it and see*. – David Heffernan Dec 06 '10 at 23:48
  • 1
    @Robert: "any valid C code is valid C++ code" - no, this is not true. C++ is not a strict superset of C. It's easy to come up with examples of legitimate C code which will not compile with a C++ compiler. – Paul R Dec 06 '10 at 23:51
  • @Paul R: Could you come up with an example of code that compiles with gcc and doesn't compile with g++? – matcheek Dec 06 '10 at 23:57
  • 1
    Example code which compiles with gcc, but doesn't compile with g++: `int class;` . Another example code, without using C++ reserved keywords: `int i=&i;`. Another example code, without using C++ reserved keywords, and which compiles without warnings with `gcc -c -pedantic -ansi -W -Wall`: `char *p=(void*)0;` – pts Dec 07 '10 at 00:25
  • 1
    @matcheek, @pts: by the way, gcc does compile C++ too, if the filename looks like C++ (`.cpp`, `.cc`, etc). It just doesn't use the right linker options for C++. So whether that code "compiles with gcc" or not depends on the name of the file it's in. Those example are syntactically correct C but are not syntactically correct C++. – Steve Jessop Dec 07 '10 at 00:30
  • I've just asked this question separately. Thanks for answers – matcheek Dec 07 '10 at 00:33
  • @matcheek: one common problem is casting the returned pointer from malloc. In C you should not cast the result of malloc, but in C++ you have to, otherwise you will get a compile error. – Paul R Dec 07 '10 at 08:09