2013-05-20 2 views
3

Вы знаете, как выполнить ленивые вычисления строки, как в этом D сниппета:Лень в C++ 11

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 
} 

Есть идеи? Спасибо.

+5

Что? [[filler]] –

+1

Лучший способ делать такие вещи, как этот IMO, - это макросы, чтобы избежать создания сообщения, которое нужно передать, если это не нужно. (см. макрос 'assert') –

+1

Использование' static if' выглядит привлекательным –

ответ

5
#ifdef DEBUG 
#define log(msg) do { cout << … << endl; } while(0) 
#else 
#define log(msg) do { } while(0) 
#endif 

Существует два способа достижения лень в C++ 11: макросы и лямбда-выражения. Оба они не «ленивы» технически, а то, что называется «нормальная оценка» (в отличие от «нетерпеливой оценки»), что означает, что выражение может быть оценено любое количество раз. Поэтому, если вы переводите программу с D (или haskell) на C++, вам нужно быть осторожным, чтобы не использовать выражения с побочными эффектами (включая время вычисления) в этих выражениях.

Чтобы достичь истинной лени, вам нужно будет выполнить memoizing, что не так просто.

Для простого ведения журнала макросы в порядке.

+0

ой, только макрос? Спасибо! – phaazon

+0

Это способ получить «ленивую оценку», если вы не хотите общаться с лямбдами. Я не думаю, что вы хотите сделать это для регистрации. – Elazar

+0

Да, действительно, лямбда - это избыток для просто регистрации. Спасибо – phaazon

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 цента.

+1

Этот шаблон IfThenElse весьма полезен, но он * не * ленив. Выражение внутри 'log (exp)' * будет * оцениваться каждый раз, даже если 'NDEBUG' не определен. – Elazar

3

Вы можете смешивать макросы и лямбды, чтобы создать этот эффект

вы могли бы иметь тип, ленивый

template<class T> 
class lazy { 
    ... 
} 

и тогда вы могли бы иметь LAZY обертку, который создал один из них с помощью лямбда

#define LAZY(E) my_lazy_type<decltype((E))>([&](){ return E; }) 

Все потребности my_lazy_type - это конструктор, который принимает функцию std :: и перегрузку оператора(), который оценивает и возвращает это. При каждой оценке вы можете заменить thunk на thunk, который просто возвращает уже вычисленное значение, и, таким образом, он будет вычисляться только один раз.

Редактировать: вот пример того, о чем я говорю. Однако я хотел бы отметить, что это не прекрасный пример. он проходит вокруг кучу вещей по стоимости в стороне от ленивых, которые могут полностью победить цель сделать все это в первую очередь. Он использует mutable внутри этого, потому что мне нужно иметь возможность memoize thunk в случаях const. Это можно было бы улучшить во многих отношениях, но это достойное доказательство концепции.

#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; 
} 
+0

спасибо, но за регистрацию вещей это довольно перебор, не так ли? – phaazon

+2

Уверен, что это слишком много, я отвечал на вопрос о том, как достичь лености. Если вы цель, это просто регистрация, я не понимаю, зачем вам вообще нужна лени. – Jake

+0

@Jake Почему нужна «обертка»? Не может быть просто 'std :: shared_ptr' в' std :: function '?? – Vahagn

Смежные вопросы