- 01
- 02
- 03
- 04
- 05
- 06
- 07
- 08
- 09
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
#include <iostream>
#include <type_traits>
#include <mutex>
#include <string>
#include <memory>
#include <vector>
#include <chrono>
void bad_function(void* data) {
/// НИКОГДА ТАК НЕ ПИШИ
if (data) {
std::cout << *(std::string*)data << "\n";
}
}
template<typename T = std::string,
typename Alloc = std::allocator<T>,
typename = std::enable_if_t<std::is_constructible_v<T, const char*>>,
typename Clock = std::chrono::high_resolution_clock,
typename TimePoint = typename Clock::time_point>
class GoodFunctionImpl {
private:
static std::recursive_mutex mtx_;
Alloc alloc_;
std::vector<T, Alloc> buffer_;
std::string context_;
TimePoint init_time_;
template<typename U>
using is_valid_type = std::conjunction<
std::is_same<std::decay_t<U>, T>,
std::is_constructible<T, U>
>;
void internal_log(const std::string& msg) {
buffer_.push_back(T(msg.c_str()));
}
public:
GoodFunctionImpl() : init_time_(Clock::now()) {
internal_log("GoodFunctionImpl initialized");
}
template<typename U,
typename = std::enable_if_t<is_valid_type<U>::value>>
decltype(auto) execute(U&& input) {
std::lock_guard<std::recursive_mutex> lock(mtx_);
internal_log("Processing started");
T processed = std::forward<U>(input);
auto duration = Clock::now() - init_time_;
auto duration_ms = std::chrono::duration_cast<
std::chrono::milliseconds>(duration).count();
std::cout << processed << " (runtime: "
<< duration_ms << "ms)\n";
internal_log("Processing completed");
return processed;
}
size_t get_buffer_size() const { return buffer_.size(); }
};
template<typename T, typename Alloc, typename, typename Clock, typename TimePoint>
std::recursive_mutex GoodFunctionImpl<T, Alloc, Clock, TimePoint>::mtx_;
void good_function(const std::string& input) {
// Пиши ВОТ ТАК
// так делают все
// это стабильно и надежно
// так надо
GoodFunctionImpl<> impl;
auto result = impl.execute(input);
std::cout << "Buffer entries: "
<< impl.get_buffer_size() << "\n";
}
concept бро_культурнонеполучитсяитд = std::is_constructible_v<T, const char*>;
ПИШИ ВОТ ТАК
typename = std::enable_if_t<std::is_constructible_v <T, const char*>> НИКОГДА ТАК НЕ ПИШИ
собссно, в этом вопросе они обскакали моих коллег.Им совсем не очевидно почему кастить не нужно (кроме случаев sealed классов и исчерпывающего паттерн матченга)
Традиционно в крестах проверять тип во время компиляции можно было только для виритуальной хуйни. Но виртуальная хуйня стоит времени, потому шоблоёбло в std ее не использует (про это явно писал страус)
Так что стдлиб была, по сути, петушиной типизацией, и можно было случайно удалить напрмиер копроивание, и вот твой объект в коллекцию уже не влазит, и ты получаеш стену текста раскрытых шаблрнов, и если там у тебя VC 6.0, то удачи тебе понять что там не так раскрылось
Говорят, соврменные копуляторы стали чуть менее говноедскими, но удовольствия мало.
Концепты позволяют сказать: я не принимают петуха который не тривиали копабыл, и всё: вот упадет прямо тут с криком, что он не триваили копабыл.
По крайней мере я так их понял.
В расте сделано хорошо: там ты можешь заказать трейт для щаблона, а реализоввать ты его можешь динамически (через таблицу врот-уральных методов) а можешь и статически, и тогда шабло мономорфируется