Search code examples
c++law-of-demeter

How to lazy load while obeying Law of Demeter?


I would like to obey Law of Demeter. But I would also like to lazy load some objects passed to constructors. How should I implement that? Pass a wrapper class? Pass a function pointer?


Solution

  • You can actually write a generic wrapper that accomplish this:

    template <typename T>
    class Lazy {
    public:
      explicit Lazy(T const& t): _loader(), _item(t) {}
      explicit Lazy(T&& t): _loader() _item(t) {}
      explicit Lazy(std::function<T()> l): _loader(l), _item() {}
    
      T& operator*() { return this->get(); }
      T const& operator*() const { return this->get(); }
    
      T* operator->() { return &this->get(); }
      T const* operator->() const { return &this->get(); }
    
    private:
      T& get() { if (not _item) { _item = _loader(); } return *_item; }
      T const& get() const { if (not _item) { _item = _loader(); } return *_item; }
    
      std::function<T()> _loader;
      mutable boost::optional<T> _item;
    }; // class Lazy
    

    This class guarantees that the loader is executed once, as long as it completes. If it does not complete, then execution will be retried on the next access. It is not thread-safe.

    Usage:

    // Output prime numbers as text
    bool isPrime(int);
    std::string toEnglish(int);
    
    void print(int i, Lazy<std::string> const& heavy) {
        if (not isPrime(i)) { return; }
    
        std::cout << *heavy << "\n";
    }
    
    int main(int argc, char* argv[]) {
        if (argc < 2) { return 1; }
    
        int max = atoi(argv[1]);
        for (int i = 2; i <= max; ++i) {
            print(i, [=](){ return toEnglish(i); });
                  // ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++11 lambda syntax
        }
    }
    

    Here, toEnglish is only ever invoked for prime numbers.