samedi 14 mars 2020

Static polymorphism with dependent internal class

I have the following - simplified -- C++11 code used to abstract away reading from an object store or from the file system:

  • ObjectLikeReader::open is a virtual function returning a Handle implementation.
  • Handle::read reads something from the underlying object.
class Handle {
   public:
    virtual std::string read() = 0;
    virtual ~Handle() = default;
};

class ObjectLikeReader {
   public:
    virtual std::unique_ptr<Handle> open() = 0;
    virtual ~ObjectLikeReader() = default;
};

// Specialization

class FileReader : public ObjectLikeReader {
   public:
    std::unique_ptr<Handle> open() override;

   private:
    class InternalHandle;
};

class FileReader::InternalHandle : public Handle {
   public:
    InternalHandle(){};
    std::string read() override {
        return "hello";
    }
};

std::unique_ptr<Handle> FileReader::open() {
    return std::unique_ptr<Handle>(new InternalHandle());
}

std::string read(ObjectLikeReader* reader) {
    return reader->open()->read();
}

int main() {
    FileReader r;
    std::cout << read(&r) << std::endl;
}

All the dispatch is done using virtual functions, and working perfectly fine. Apart from performance in tight loop, which commands having a look at static polymorphism:

#include <iostream>
#include <string>

template <class T>
class Handle {
   public:
    std::string read() {
        return static_cast<T*>(this)->read();
    }

   protected:
    ~Handle() = default;
};

template <class T, class H>
class ObjectLikeReader {
   public:
    Handle<H> open() {
        return static_cast<T*>(this)->open();
    }

   protected:
    ~ObjectLikeReader() = default;
};

// Specialization

class FileReaderHandle : Handle<FileReaderHandle> {
   public:
    FileReaderHandle(){};
    std::string read() {
        return "hello";
    }
};

class FileReader : public ObjectLikeReader<FileReader, FileReaderHandle> {
   public:
    FileReaderHandle open() {
        return FileReaderHandle();
    }
};

int main() {
    FileReader r;
    std::cout << r.open().read() << std::endl;
}

I am not quite satisfied of this implementation. Is there a way to achieve the same functionality as the second example?:

  • Without requiring the double template parameter (traits maybe? not sure...),
  • Ideally without having to make the handle class public.

Thanks!


Edit: Fixed stupid missing constructor definition causing compilation error.

Aucun commentaire:

Enregistrer un commentaire