У меня есть задание для создания практически универсального c класса, который получает некоторый входной источник целых чисел и своего рода потребителя, который потребляет эти входные данные. Мне удалось сделать эту работу, но это ужаснее, чем я хочу. Так что это часть моего решения, которое работает.
Это интерфейс, который реализуют мои источники ввода:
class InputSource {
public:
virtual const InputSource& operator<<(int& num) const = 0;
virtual ~InputSource()=default;
};
Это одна реализация, которая получает ввод с клавиатуры:
class KeyboardInput : public InputSource {
public:
KeyboardInput()=default;
virtual ~KeyboardInput()=default;
virtual const InputSource& operator<<(int& num) const override {
std::cin >> num;
return *this;
}
};
И это моя реализация класса числовой последовательности, которая принимает некоторый входной источник и выполняет действие, которое является функцией std ::, которая работает с числами, которые даны из входного источника
class NumberSequence {
const InputSource &input_source;
const std::function<void(int)> &action;
int next_num() {int num; input_source<<num; return (num<0 ? -1 : num);} // -1 if no morenumbers
public:
NumberSequence(const InputSource &input_source, const std::function<void(int)> &action) : input_source(input_source), action(action) {}
void start_action() {
int num;
do {
action(num = next_num());
if(num == -1) break;
std::this_thread::sleep_for(std::chrono::seconds(1));
} while(true);
}
};
Мясо этого класса является функцией-членом start_action, которая получает входные данные от заданного входного источника, затем вызывает действие с этим номером и ожидает 1 se c и делает это, пока входной источник не выдаст -1, просто так.
Итак, теперь я написал реализацию одного действия, которое выводит эти числа в файл, но не как класс, а как лямбда , например:
static auto write_to_file_action = [](std::ofstream& output_file) {
return [&output_file](int num){
if(num == -1) return;
using namespace std::chrono;
time_point<system_clock> now = system_clock::now();
std::time_t time = system_clock::to_time_t(now);
output_file << num <<"\t"<< std::ctime(&time) << std::endl;
};
};
Эта лямбда принимает std :: ofstream к файлу, в который я выводю числа, плюс это добавляет время, но это не очень важно (часть со временем). Таким образом, использование этого в моей основной функции выглядит примерно так:
int main(void) {
KeyboardInput input_source;
std::ofstream output_file("output_file.txt");
NumberSequence num_seq(input_source, write_to_file_action(output_file));
num_seq.start_action();
return 0;
}
Как я уже сказал это работает , но я хотел бы иметь что-то вроде этого:
int main(void) {
KeyboardInput input_source;
NumberSequence num_seq(input_source, write_to_file_action("output_file.txt"));
num_seq.start_action();
return 0;
}
Это выглядит так просто, но мне трудно это реализовать. Я попытался реализовать write_to_file_action следующим образом:
static auto write_to_file_action = [](const char* file_name) {
std::ofstream output_file(file_name);
return [output_file = std::move(output_file)](int num) mutable {
if(num == -1) return;
using namespace std::chrono;
time_point<system_clock> now = system_clock::now();
std::time_t time = system_clock::to_time_t(now);
output_file << num <<"\t"<< std::ctime(&time) << std::endl;
};
};
Но затем я получаю ошибку компиляции, которая в основном говорит, что это не будет работать, потому что мой класс NumberSequence хочет std :: function, а std :: function должен быть копируемый и это не мой случай. В закрытии моей внутренней лямбды у меня есть std :: ofstream, который не копируется .
Так что я попытался шаблонировать свой класс NumberSequence так:
template<typename Func>
class NumberSequence {
const InputSource &input_source;
const Func action;
int next_num() {int num; input_source<<num; return (num<0 ? -1 : num);} // -1 if no more numbers
public:
NumberSequence(const InputSource &input_source, Func &&action)
: input_source(input_source), action(std::move(action)) {}
void start_action() {
int num;
do {
action(num = next_num());
if(num == -1) break;
std::this_thread::sleep_for(std::chrono::seconds(1));
} while(true);
}
};
Это не скомпилируется, но теперь он не компилируется, потому что он говорит, что отсутствует аргумент шаблона перед переменной num_seq в main (это можно вывести), поэтому я могу сделать что-то вроде этого:
int main(void) {
KeyboardInput input_source;
auto lambda = write_to_file_action("output_file.txt");
NumberSequence<decltype(lambda)> num_seq(input_source, lambda);
num_seq.start_action();
return 0;
}
И мне также нужно создать другой конструктор в моей NumberSequence, который принимает обычную ссылку на действие, а не ссылку на значение. Как мне грустно, это работает, но я хотел бы удалить это явное создание шаблона. Я не знаю, возможно ли это, но я думаю, что это возможно. Если кто-то может объяснить, почему он не может вывести тип неявным образом, потому что я его не понимаю. Спасибо.
PS Извините за длинное сообщение, я пишу впервые, и я хотел охватить весь контекст моей проблемы.