Changing a string macro at compile time
I am developing a unique client that needs to work on different machines. On each computer, the server runs on a different IP address, but this address is known.
I don't want to tell the client which IP is every time I start it, so I tell it at compile time.
The problem is when compiling with g++ -DHOSTNAME=127.0.0.1
(also with double quotes) the compiler says:
error: too many decimal points in number
./include/Client.h:18:25: note: in expansion of macro ‘HOSTNAME’
I tried it too using localhost.
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)
Compilation 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 think that maybe macros are not the best way to handle this, but I don't understand how.
If anyone has any links I would appreciate it.
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);
}
source to share
You need to avoid double quotes:
g++ -DHOSTNAME=\"127.0.0.1\"
Otherwise, the quotes just tell your shell what 127.0.0.1
is the value you want to supply -DHOSTNAME
, which can be useful if the value has spaces, for example:
g++ -DMAGIC_NUMBER="150 / 5"
(there, MAGIC_NUMBER
will be replaced with 150 / 5
no quotes)
If you want the quotes to be part of the macro (as in #define HOSTNAME "127.0.0.1"
), you have to tell your shell that they are part of the value you give -DHOSTNAME
, this is done by escaping.
EDIT
Also, as Angew pointed out, you misused the XSTR trick. This is a different solution to your problem than my answer.
It certainly works like this:
#define XSTR(x) STR(x)
#define STR(x) #x
With this, you don't need to hide the quotes.
These two macros change the text 127.0.0.1
to "127.0.0.1"
. The macro XSTR
allows HOSTNAME
expansion before 127.0.0.1
the macro STR
converts it to "127.0.0.1"
. If you used a macro directly STR
, you should have "HOSTNAME"
instead "127.0.0.1"
.
I think I prefer the escaping solution to use the trick involving two macros in the code, but that works too.
source to share
It seems strange that you want to write it to an executable file. It should be more flexible to use something like getenv("MY_SERVER_ADDR")
and just set this environment variable before starting your server. Or, of course, you could do a more typical thing and think of it as a command line argument, but something tells me that you've already decided not to.
A somewhat strange idea, if you are on Linux, is to write the IP address to a text file and create an ELF object file using ld
and objcopy
; you can load this application as a generic object, or even static if you really want to "hard-code" it. But I'm not sure why this would be preferable to the previously mentioned options.
source to share