Вы знаете, как выполнить ленивую оценку строки, как в этом фрагменте D:
void log(lazy string msg) {
static if (fooBarCondition)
writefln(…) /* something with msg */
}
На самом деле, проблема может не нуждаться в лени вообще, так как статический если. Может быть, можно отказаться char const*
Строки, когда не используются? Мол, в C ++:
void log(char const *msg) {
#ifdef DEBUG
cout << … << endl; /* something with msg */
#else /* nothing at all */
#endif
}
Любая идея? Спасибо.
#ifdef DEBUG
#define log(msg) do { cout << … << endl; } while(0)
#else
#define log(msg) do { } while(0)
#endif
Есть два способа добиться лени в C ++ 11: макросы и лямбда-выражения. Оба они не «ленивы» технически, но то, что называется «нормальной оценкой» (в отличие от «нетерпеливой оценки»), что означает, что выражение может быть оценено любое количество раз. Поэтому, если вы переводите программу с D (или haskell) на C ++, вам следует быть осторожным, чтобы не использовать выражения с побочными эффектами (включая время вычислений) в этих выражениях.
Чтобы добиться истинной лени, вам придется использовать запоминание, что не так просто.
Для простой регистрации макросов просто отлично.
Вы можете смешать макросы и лямбды, чтобы создать этот эффект
Вы могли бы иметь тип, ленивый
template<class T>
class lazy {
...
}
и тогда вы могли бы иметь ленивую оболочку, которая создала один из них, используя лямбду
#define LAZY(E) my_lazy_type<decltype((E))>([&](){ return E; })
Все, что нужно my_lazy_type, — это конструктор, который принимает функцию std ::, и перегрузка operator (), которая оценивает и возвращает это. В каждой оценке вы можете заменить thunk на thunk, который просто возвращает уже вычисленное значение, и, таким образом, он будет вычислен только один раз.
редактировать:
Вот пример того, о чем я говорю. Однако я хотел бы отметить, что это не идеальный пример. он обходит кучу вещей по значению в сторону ленивых, которые могут полностью победить цель сделать все это в первую очередь. Он использует изменяемый внутри этого, потому что мне нужно иметь возможность запоминать Thunk в постоянных случаях. Это можно улучшить многими способами, но это достойное доказательство концепции.
#include <iostream>
#include <functional>
#include <memory>
#include <string>
#define LAZY(E) lazy<decltype((E))>{[&](){ return E; }}
template<class T>
class lazy {
private:
struct wrapper {
std::function<T()> thunk;
wrapper(std::function<T()>&& x)
: thunk(std::move(x)) {}
wrapper(const std::function<T()>& x)
: thunk(x) {}
};
//anytime I see mutable, I fill a bit odd
//this seems to be warented here however
mutable std::shared_ptr<wrapper> thunk_ptr;
public:
lazy(std::function<T()>&& x)
: thunk_ptr(std::make_shared<wrapper>(std::move(x))) {}
T operator()() const {
T val = thunk_ptr->thunk();
thunk_ptr->thunk = [val](){return val;};
return val;
}
};
void log(const lazy<std::string>& msg) {
std::cout << msg() << std::endl;
}
int main() {
std::string hello = "hello";
std::string world = "world";
log(LAZY(hello + ", " + world + "!"));
return 0;
}
В то время как ответ Элазара работает, я предпочитаю не использовать для этого макросы (особенно не с строчными именами).
Вот что я бы сделал вместо этого:
template<bool /* = false */>
struct logger_impl {
template<typename T>
static std::ostream & write(std::ostream & stream, T const &) {
return stream;
}
};
template<>
struct logger_impl<true> {
template<typename T>
static std::ostream & write(std::ostream & stream, T const & obj) {
return stream << obj;
}
};
template<typename T>
void log(T const & obj) {
#if defined(NDEBUG)
logger_impl<true>::write(std::cout, obj);
#else
logger_impl<false>::write(std::cout, obj);
#endif
}
Просто мои 2 цента.