ShakMR
ShakMR

Reputation: 115

Change a string macro at compilation time

I'm developing an unique client that has to work on different machines. In every machine the server is running in a different IP address, but this address is known.

I don't want to tell the client which is the IP every time I run it, so I though about tell it in compilation time.

The problem is that when compiling with g++ -DHOSTNAME=127.0.0.1 (also tried with double quotes) the compiler is saying:

error: too many decimal points in number
./include/Client.h:18:25: note: in expansion of macro ‘HOSTNAME’

I tried it using localhost, too.

error: ‘localhost’ was not declared in this scope
./include/Client.h:18:25: note: in expansion of macro ‘HOSTNAME’

Also tried using some things found on the internet.

#define XSTR(x) STR(x)
#define STR(x)

compile error:

./src/BSCClient.cpp:15:45: note: #pragma message: HOSTNAME: 
#pragma message("HOSTNAME: " XSTR(HOSTNAME))

./src/BSCClient.cpp:16:39: error: too few arguments to function ‘hostent* gethostbyname(const char*)’
  server = gethostbyname(XSTR(HOSTNAME));

At this point I'm thinking that maybe macros isn't the proper way to handle this, but I don't figure out how to do it.

If someone has any reference about it I will be thankful.

EDIT: These are the codes.

Client.h:

#ifndef __CLIENT_HH__
#define __CLIENT_HH__

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

#include <string>
#include <iostream>

using namespace std;

#define HOSTNAME 127.0.0.1
#define MAX_MESSAGE_LENGTH 10 

class Client {
private:
    string client_name;
    int sockfd, portno;
    struct sockaddr_in serv_addr;
    struct hostent *server;
    error(const char *msg);

public:

    BSCClient (string name, int port);
    void identifyme();
    void sendData (string data);
    string recvData ();

    void closeSocket();
};

#endif

Client.cpp

#include "BSCClient.h"

#include <stdlib.h>
#include <time.h>

void BSCClient::error(const char *msg)
{
    perror(msg);
    exit(0);
}

Client::Client(string name, int port)
{
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    portno = port;
    client_name = name;

    if (sockfd < 0) 
        error("ERROR opening socket");

    server = gethostbyname(HOSTNAME);

    if (server == NULL) {
        fprintf(stderr,"ERROR, no such host\n");
        exit(0);
    }

    bzero((char *) &serv_addr, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    bcopy((char *)server->h_addr, 
         (char *)&serv_addr.sin_addr.s_addr,
         server->h_length);
    serv_addr.sin_port = htons(portno);
    if (connect(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) 
        error("ERROR connecting");

    sendData(client_name);
}

void Client::identifyme() {
    FILE *fp;
    fp = popen("id -gn", "r");

    char text[6];
    fscanf(fp, "%s", text);
    pclose(fp);
    string data(text);
    sendData(data);
}

void Client::sendData (string data) {
    const char *sdata = data.c_str();
    int n;
    n = write(sockfd, sdata, strlen(sdata));
    if (n < 0) 
         error("ERROR writing to socket");
}

string Client::recvData () {
        int n;
        int bytes;
        char *longitud = new char[MAX_MESSAGE_LENGTH+1];
        n = read(sockfd, longitud, MAX_MESSAGE_LENGTH);
        if (n < 0) {
                error("ERROR recieving size of output");
        }
        bytes=atoi(longitud);
        //Para forzar el fin del string (ya que al imprimir el string hay veces que muestra caracteres de más)
        longitud[MAX_MESSAGE_LENGTH]='\0';
        char *data = new char[bytes];
        n = read(sockfd, data, bytes);
        if (n < 0)
                error("ERROR reading output");
        string ret(data);
        return ret;
} 

void Client::closeSocket() {
    close(sockfd);
}

Upvotes: 4

Views: 4105

Answers (2)

Dettorer
Dettorer

Reputation: 1336

You have to escape the double quotes:

g++ -DHOSTNAME=\"127.0.0.1\"

Otherwise, the quotes are just saying to your shell that 127.0.0.1 is the value you want to give to -DHOSTNAME, which can be useful if the value has whitespaces for example:

g++ -DMAGIC_NUMBER="150 / 5"

(there, MAGIC_NUMBER will be replaced by 150 / 5 without the quotes)

If you want the quotes to be part of the macro (as in #define HOSTNAME "127.0.0.1"), you have to say to your shell that they are part of the value you give to -DHOSTNAME, this is done by escaping them.

EDIT:

Also, as pointed out by Angew, you misused the XSTR trick. It is an other solution to your problem than my answer.

It certainly works like this:

#define XSTR(x) STR(x)
#define STR(x) #x

With that you don't have to escape the quotes.

These two macros change the text 127.0.0.1 into "127.0.0.1". The XSTR macro allows HOSTNAME to be expanded to 127.0.0.1 before the STR macro converts it to "127.0.0.1". If you used directly the STR macro, you would end up with "HOSTNAME" instead of "127.0.0.1".

I think I prefer the escaping solution to the use of a trick involving two macros in the code, but that works too.

Upvotes: 9

John Zwinck
John Zwinck

Reputation: 249093

It seems odd that you'd want to hard-code this into the executable. It should be more flexible to use something like getenv("MY_SERVER_ADDR") and just set that environment variable before running your server. Or of course you could do the more typical thing and take it as a command line argument, but something tells me you already decided not to do that.

A slightly weirder idea if you are on Linux is to write the IP address into a text file and create an ELF object file from that using ld and objcopy; you can then load this into your app as a shared object or even a static one if you really want to "hard code" it. But I'm not sure why this would be preferable to the previously mentioned options.

Upvotes: 0

Related Questions