0

我真的很喜欢 %s,ID 的概念。我注意到这适用于 char,并且我知道这是 C 风格的功能。我想知道是否还有其他方法可以用字符串实现 %s:

这是我的代码:

#include "services.h"

#include <iostream>
using namespace std;

services::services()
{
}


services::~services()
{
}

int services::startup()
{

#if defined(WINDOWS)
    {
        WSAData data;

         if(WSAStartup(MAKEWORD(2,2), &data) != 0)
             exit(1);
    }
#endif

    int addresssize = sizeof(address);

    address.sin_addr.s_addr = inet_addr("69.60.118.163");
    address.sin_family = AF_INET;
    address.sin_port = htons(6667);

    sock = socket(AF_INET,SOCK_STREAM,0);

    if(sock < 0)
        return -1;

    string PASSWORD = "password1";
    string SERVER_NAME = "admin.services.net";
    string SERVER_DESC = "Administration IRC Services";


    if(connect(sock,(struct sockaddr *)&address,addresssize) == 0)
    {
        send_cmd("PASS ","%s\n",PASSWORD);
        send_cmd("SERVER ","%s 1 %s\n",SERVER_NAME,SERVER_DESC);

     return 0;
    }

    return -1; //ooops
}

void services::parseData()
{
    char buffer[512];
    stringstream convert;
    string newbuffer;
    for(;;)
    {
        int index = recv(sock,buffer,512-1,0);
        buffer[index]='\0';
        newbuffer = buffer;

        cout << newbuffer.c_str() << endl;

        if(index == 0)
        {
            cout << "Connection was closed!" << endl;
            break;
        }

        system("pause");

    }
}

void services::send_cmd(string cmd)
{



}

void services::send_cmd(string cmd, string param,...)
{
    int bufsize;
    string newmsg;

        newmsg = (cmd += param);

        cout << newmsg.c_str() << endl;

        bufsize = sizeof(newmsg);

        send(sock,newmsg.c_str(),strlen(newmsg.c_str()),0);
}

现在的问题是 send_cmd int 他的启动功能(在连接检查中)实际上给出了 %s 而不是密码/服务器信息的值

它只是在控制台中显示为“PASS %s”或“SERVER %s”——正如我所说,我喜欢 %s 的概念是否有其他方法可以实现这一点,或者只是使用send_cmd("PASS",PASSWORD);它(效果很好,我只是挑剔)

4

3 回答 3

5

这里有几种可能性。一种是稍微修改现有函数,使用 vsprintf,这样它就可以接受任意数量的参数并进行如下替换printf

void services::send_cmd(string cmd, char const *param,...)
{
    char buffer[512];

    va_list args;
    va_start(param, args);
    vsprintf(buffer, args, param);
    va_end(args);
    cmd += buffer;
    send(sock, cmd.c_str(),cmd.length(),0);
}

但是,这与所有采用可变参数列表的函数都有一个问题:将std::string(或任何其他非 POD 类类型的对象)作为可变参数之一传递会导致未定义的行为。

如果您希望能够通过std::strings,您还有其他几种可能性。更简单的方法是使用流将数据写入套接字。为此,您将编写一个(谢天谢地,相当简单)流缓冲区类,它将数据写入套接字而不是更典型的目标,如磁盘上的文件。这并不是很难做到,但是当我什至不确定你是否想要它时,发布它肯定是太多了。

第三种可能性是使用可变参数模板来支持处理各种类型的参数。这消除了传递格式字符串的麻烦,因为它可以直接从参数中推断出类型:

#include <sstream>
#include <string>
#include <iostream>

template <class T>
std::string stringify(T const &t) { 
    std::stringstream b;
    b << t;
    return b.str();
}

template<typename T, typename... Args>
std::string stringify(T arg, const Args&... args) {
    return stringify(arg) + stringify(args...);
}

template<typename... Args>
void send_cmd(const Args&... args) { 
    std::string buffer = stringify(args...);
    send(sock, buffer.c_str(), buffer.length(), 0);
}

int main() {
    std::string three{" three"};

    send_cmd("one: ", 1, " two: ", 2, three, "\n");

    return 0;
}

在 C++17 中,这可以使用折叠表达式大大简化,所以它变成了这样:

template<typename... Args>
void send_cmd(int sock, const Args&... args) {
    std::ostringstream os;
    (os << ... << args);
    std::string const &s = os.str();
    send(sock, s.c_str(), s.length(), 0);
}

int main() {
    std::string three{ " three" };

    int x = socket();

    // Of course, here you'll need to connect the socket to use it.

    send_cmd(x, "one: ", 1, " two: ", 2, three, "\n");

    return 0;
}

std::string在演示代码中传递了一些字符串文字、整数和 a,但基本上任何支持类似内容的类型都cout << whatever应该可以正常工作。

于 2013-06-26T03:23:44.917 回答
1

您可以使用 sprintf 转换为字符串,然后将 send_cmd 修改为只有两个参数。

于 2013-06-26T02:59:28.340 回答
0

您可以使用 boost 格式库来准备您的输出 - http://www.boost.org/doc/libs/1_53_0/libs/format/doc/format.html - 类似于:

send_cmd("SERVER ", format("%1% 1 %2%\n", SERVER_NAME, SERVER_DESC).str());
于 2013-06-26T03:56:54.847 回答