代码之家  ›  专栏  ›  技术社区  ›  ivme

用c语言构建封装但可扩展的动画库++

  •  0
  • ivme  · 技术社区  · 7 年前

    1. 建模和渲染分离 。有关场景状态的信息应与渲染场景的过程分开存储。
    2. 。如果库本身定义了 node 类,库的用户应该能够定义新类型 custom_node 节点 自定义_节点 附加
    3. 库的封装。 将库的功能扩展到自定义 节点 用户不需要编辑库的底层代码。

    失败的方法:使用 s作为场景的模型。子类 节点 vector<std::shared_ptr<node>> . renderer 类和子类 渲染器

    class image;
    
    class node {
        virtual image render(renderer &r) {return r.render(*this);}
        std::vector<std::shared_ptr<node>> children;
        std::weak_ptr<node> parent;
        // ...
    }
    
    class renderer {
        image render(node &n) {/*rendering code */}
    // ...
    }
    

    renderer r{};
    

    并使用您最喜欢的遍历方法遍历节点树。当你遇到每一个 std::shared_ptr<node> n 呼叫

    n->render(r);
    

    这种方法将建模和渲染分离开来,并允许扩展性。创建 节点

    class custom_node : public node {
        virtual image render(renderer &r) override {return r.render(*this)}
    }
    

    .为此,我们尝试子类化 渲染器 render 方法:

    class custom_renderer : public renderer {
        image render(custom_node &n) {/*custom rendering code*/}
    }
    

    就其本身而言,这是行不通的。考虑:

    renderer &r = custom_renderer{};
    std::shared_ptr<node> n = std::make_shared<custom_node>{};
    n->render(r); // calls renderer::render(node &)
    

    为了根据需要调用custom\u renderer::render(custom\U node&n),我们需要向原始的renderer类添加一个虚拟重载:

    class renderer {
        image render(node &n) {/*rendering code */}
        virtual image render(custom_node &n) = 0;
    }
    

    不幸的是,这破坏了库的封装,因为我们编辑了一个库类。

    2 回复  |  直到 7 年前
        1
  •  0
  •   Yakk - Adam Nevraumont    7 年前

    键入擦除。该库提供渲染(某些_数据)函数。

    我们从几种节点开始。基本体是渲染(基本体)仅绘制某些内容的节点。

    列表节点有子节点,渲染(List_节点)绘制其内容。

    generic_节点存储具有渲染(?)的任何内容超载。它会删除渲染(?)活动调用render(generic_节点)对包含的数据调用该类型的擦除操作。

    list_节点包含泛型_节点的向量。

    为了添加新的渲染类型,您只需定义一个新类型,重载渲染(new\u type),然后将其存储在泛型节点中。

    struct render_target {
      // stuff about the thing we are rendering on
    };
    struct renderable_concept {
      virtual ~renderable_concept() {}
      virtual void render_on( render_target* ) const = 0;
    };
    template<class T>
    void render( render_target*, T const& ) = delete; // by default, nothing renders
    
    struct emplace_tag {};
    template<class T>
    struct renderable_model : renderable_concept {
      T t;
      template<class...Us>
      renderable_model( emplace_tag, Us&&...us ):
        t{std::forward<Us>(us)...}
      {}
      void render_on( render_target* target ) const final override {
        render( target, t );
      }
    };
    template<class T>
    struct emplace_as {};
    struct generic_node {
      friend void render( render_target* target, generic_node const& node ) {
        if (!node.pImpl) return;
        node.pImpl->render_on(target);
      }
      template<class T, class...Us>
      generic_node( emplace_as<T>, Us&&... us):
        pImpl( std::make_shared<renderable_model<T>>(emplace_tag{}, std::forward<Us>(us)...) )
      {}
      generic_node() = default;
      generic_node(generic_node&&)=default;
      generic_node(generic_node const&)=default;
      generic_node& operator=(generic_node&&)=default;
      generic_node& operator=(generic_node const&)=default;
    private:
      std::shared_ptr<renderable_concept> pImpl;
    };
    

    现在,如何创建列表节点。

    struct list_node {
      std::vector<generic_node> nodes;
      friend void render( render_target* target, list_node const& self ) {
        for (auto&& node:self.nodes)
          render(target, node);
      }
      list_node(std::vector<generic_node> ns):nodes(std::move(ns)) {}
      list_node() = default;
      list_node(list_node&&)=default;
      list_node& operator=(list_node&&)=default;
    };
    
    template<class T, class...Args>
    generic_node make_node( Args&&... args ) {
      return {emplace_as<T>{}, std::forward<Args>(args)...};
    }
    template<class T>
    generic_node make_node( T&& t ) {
      return {emplace_as<std::decay_t<T>>{}, std::forward<T>(t) };
    }
    

    struct printing_node {
      std::string message;
      friend void render( render_target* target, printing_node const& self ) {
        std::cout << self.message;
      }
    };
    

    测试代码:

    auto list = make_node( list_node{{
      make_node( printing_node{{"hello"}} ),
      make_node( printing_node{{"world"}} )
    }});
    render_target target;
    render(&target, list);
    

    Live example .

        2
  •  0
  •   ivme    7 年前

    我自己的解决方案,是Yakk提出的类型擦除方法的变体。可以找到有关该问题和此特定方法的更多详细信息 here .

    struct image{};
    
    struct renderable_concept {
      virtual image render() const = 0;
    };
    
    template <class WRAPPED, class RENDERER>
    struct renderable_model : public renderable_concept {
      WRAPPED *w;
      RENDERER r;
      virtual image render() const final override {
        return r.render(*w);
      }
      renderable_model(WRAPPED *w_, RENDERER r_) : w(w_), r(r_) {}
    };
    
    struct node {
      template <class WRAPPED, class RENDERER>
      node(WRAPPED *w_, RENDERER r_) :
        p_renderable(new renderable_model<WRAPPED,RENDERER>(w_,r_)) {}
    
      template <class RENDERER>
      node(RENDERER r_) : node(this,r_) {}
    
      image render() {return p_renderable->render();}
      vector<shared_ptr<node>> children;
      unique_ptr<renderable_concept> p_renderable;
    };
    
    struct text_node : public node {
      template<class RENDERER>
      text_node(RENDERER r) : node(this,r) {}
    
      string val;
    };
    
    struct shape_node : public node {
      template<class RENDERER>
      shape_node(RENDERER r) : node(this,r) {}
    };
    
    struct color_renderer {
      image render(node &) const {/*implementation*/};
      image render(text_node &) const {/*implementation*/};
      image render(shape_node &) const {/*implementation*/};
    };
    
    struct grayscale_renderer {
      image render(node &) const {/*implementation*/};
      image render(text_node &) const {/*implementation*/};
      image render(shape_node &) const {/*implementation*/};
    };