【问题标题】:Change a string macro at compilation time在编译时更改字符串宏
【发布时间】:2014-11-30 12:37:50
【问题描述】:

我正在开发一个必须在不同机器上工作的独特客户端。在每台机器上,服务器运行在不同的 IP 地址中,但这个地址是已知的。

我不想每次运行时都告诉客户端哪个是IP,所以我想在编译时告诉它。

问题在于,当使用g++ -DHOSTNAME=127.0.0.1(也尝试使用双引号)编译时,编译器会说:

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

我也尝试使用 localhost。

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

还尝试使用在互联网上找到的一些东西。

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

编译错误:

./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));

在这一点上,我在想也许宏不是处理这个问题的正确方法,但我不知道该怎么做。

如果有人对此有任何参考,我将不胜感激。

编辑: 这些是代码。

客户端.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

客户端.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);
}

【问题讨论】:

  • Client.h:18 上的实际代码是什么?
  • @Niall 不,它说的一样。对不起,我忘了写我已经试过了。
  • @BorjaArias,好的,除了您已经发布的错误内容之外,发布产生错误的实际代码也可能很有用。
  • 除非您甚至没有正确复制粘贴 XSTR 技巧。第二行应该是#define STR(x) #x。当然,你不应该盲目地将你不懂的互联网代码粘贴到生产代码中。

标签: c++ string c++11 macros compilation


【解决方案1】:

您想将其硬编码到可执行文件中似乎很奇怪。使用getenv("MY_SERVER_ADDR") 之类的东西应该更灵活,只需在运行服务器之前设置该环境变量即可。或者当然你可以做更典型的事情并将其作为命令行参数,但有件事告诉我你已经决定不这样做了。

如果你在 Linux 上,一个稍微奇怪的想法是将 IP 地址写入一个文本文件,然后使用 ldobjcopy 创建一个 ELF 对象文件;然后,如果您真的想“硬编码”它,您可以将其作为共享对象甚至静态对象加载到您的应用程序中。但我不确定为什么这比前面提到的选项更可取。

【讨论】:

  • 我在多用户系统中工作,因此出于安全原因,我宁愿避免使用环境变量。
  • 用户无法写入的配置文件(可以是包含主机名的单行文件)怎么办?
【解决方案2】:

你必须转义双引号:

g++ -DHOSTNAME=\"127.0.0.1\"

否则,引号只是对您的 shell127.0.0.1 是您要赋予 -DHOSTNAME 的值,如果该值包含空格,这可能很有用,例如:

g++ -DMAGIC_NUMBER="150 / 5"

(那里,MAGIC_NUMBER 将被替换为 150 / 5,不带引号)

如果您希望引号成为宏的一部分(如在#define HOSTNAME "127.0.0.1" 中),您必须对您的shell 说它们是您赋予-DHOSTNAME 的值的一部分,这是通过转义它们来完成的。

编辑

此外,正如 Angew 所指出的,您误用了 XSTR 技巧。这是您问题的另一种解决方案,而不是我的回答。

它确实是这样工作的:

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

这样您就不必转义引号。

这两个宏将文本127.0.0.1 更改为"127.0.0.1"XSTR 宏允许在 STR 宏将其转换为 "127.0.0.1" 之前将 HOSTNAME 扩展为 127.0.0.1。如果你直接使用STR 宏,你最终会得到"HOSTNAME" 而不是"127.0.0.1"

我认为我更喜欢转义解决方案,而不是在代码中使用涉及两个宏的技巧,但这也有效。

【讨论】:

  • 谢谢,这行得通。今天我忘记添加#x。但在过去的一周里,我确定我写了它。无论如何,现在它正在工作。
  • 转义双引号不起作用。编译器打印与不转义相同的错误。
  • 您是否完全按照我的第一个示例中的方式转义它们?另外,您是否删除了代码中的#define HOSTNAME 127.0.0.1? (如果没有,您应该会看到有关 HOSTNAME 被重新定义的警告)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-10-21
  • 1970-01-01
  • 1970-01-01
  • 2021-11-18
  • 2011-06-25
相关资源
最近更新 更多