Мне очень нравится концепция% 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 внутри функции запуска (при проверке соединения) фактически получает% s, а не значение пароля / информации о сервере
Он просто отображается в консоли как «PASS% s» или «SERVER% s» — как я уже сказал, мне нравится концепция% s, есть ли другой способ реализовать это или просто использовать send_cmd("PASS",PASSWORD);
вместо этого (который работает хорошо, я просто привередлив)
У вас есть несколько возможностей здесь. Один из них — немного изменить существующую функцию, использовать 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::string
у вас есть пара других возможностей. Проще было бы использовать поток для записи данных в сокет. Для этого вы должны написать (к счастью, довольно простой) класс потокового буфера, который записывает данные в сокет, а не в более типичное место назначения, такое как файл на диске. Это не ужасно Трудно сделать, но это определенно слишком много для публикации, когда я даже не уверен, что вы этого хотите.
Третья возможность состоит в том, чтобы использовать шаблон переменной для поддержки обработки аргументов различных типов. Это избавляет от необходимости передавать строку формата, потому что он может выводить типы непосредственно из аргументов:
#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
в демо-коде, но, по сути, любой тип, который поддерживает что-то вроде cout << whatever
должно работать нормально.
Вы можете использовать sprintf для преобразования в символьную строку, а затем изменить send_cmd, чтобы иметь только два параметра.
Вы можете использовать библиотеку формата 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());