Skip to content

C++ Source

Source code

/*
 Copyright 2013 Adobe Systems Incorporated
 Distributed under the MIT License (see license at
 http://stlab.adobe.com/licenses.html)

 This file is intended as example code and is not production quality.
 */

#include <cassert>
#include <iostream>
#include <memory>
#include <string>
#include <vector>

using namespace std;

/******************************************************************************/
// Library
template<typename T>
void draw(const T &x, ostream &out, size_t position)
{
    out << string(position, ' ') << x << endl;
}

class object_t
{
public:
    template<typename T>
    object_t(T x) :
                    self_(make_shared<model<T>>(move(x)))
    {
    }

    friend void draw(const object_t &x, ostream &out, size_t position)
    {
        x.self_->draw_(out, position);
    }

private:
    struct concept_t
    {
        virtual ~concept_t() = default;
        virtual void draw_(ostream&, size_t) const = 0;
    };
    template<typename T>
    struct model: concept_t
    {
        model(T x) :
                        data_(move(x))
        {
        }
        void draw_(ostream &out, size_t position) const
        {
            draw(data_, out, position);
        }

        T data_;
    };

    shared_ptr<const concept_t> self_;
};

using document_t = vector<object_t>;

void draw(const document_t &x, ostream &out, size_t position)
{
    out << string(position, ' ') << "<document>" << endl;
    for (auto &e : x)
        draw(e, out, position + 2);
    out << string(position, ' ') << "</document>" << endl;
}

using history_t = vector<document_t>;

void commit(history_t &x)
{
    assert(x.size());
    x.push_back(x.back());
}
void undo(history_t &x)
{
    assert(x.size());
    x.pop_back();
}
document_t& current(history_t &x)
{
    assert(x.size());
    return x.back();
}

/******************************************************************************/
// Client
class my_class_t
{
    /* ... */
};

void draw(const my_class_t&, ostream &out, size_t position)
{
    out << string(position, ' ') << "my_class_t" << endl;
}

int main()
{
    history_t h(1);

    current(h).emplace_back(0);
    current(h).emplace_back(string("Hello!"));

    draw(current(h), cout, 0);
    cout << "--------------------------" << endl;

    commit(h);

    current(h).emplace_back(current(h));
    current(h).emplace_back(my_class_t());
    current(h)[1] = string("World");

    draw(current(h), cout, 0);
    cout << "--------------------------" << endl;

    undo(h);

    draw(current(h), cout, 0);
}
// g++ --std=c++11 -Wall -pedantic test.cpp && ./a.out

如何理解"Value Semantics and Concept-based Polymorphism"?

如何理解"Value Semantics and Concept based Polymorphism"?主要是要理解:

NOTE: "Value Semantics and Concept-based Polymorphism"有如下理解方式:

1、Value Semantics、Concept-based Polymorphism

2、Value Semantics 和 Concept-based一起修饰Polymorphism

我目前是按照2来进行说明的

1、Value Semantics Polymorphism

2、Concept based Polymorphism

Concept based Polymorphism

如何理解"Concept based Polymorphism"?我是通过source code中的struct concept_tstruct model: concept_t发现答案的。

Source code中的struct concept_tstruct model: concept_t让我想起来之前阅读的如下内容:

1、More C++ Idioms/Polymorphic Value Types

这篇文章其实就是对此的介绍,其中有这样的描述:

Also Known As

Run-time Concept

上述"Concept"是经典generic programming中的概念,C++中generic programming是static的,而上述"Run-time Concept"表明它借用了generic programming的概念,实现了run-time版的concept。

2、boost Generic Programming Techniques时,其中有这样的描述:

A concept is a set of requirements consisting of valid expressions, associated types, invariants, and complexity guarantees. A type that satisfies the requirements is said to model the concept.

上面完整地介绍了concept、model之间的关系,显然它们都是GP中的概念


下面是总结:

1、结合上述内容和source code,其实大致就理解了"Concept based Polymorphism"的含义

2、需要注意的是: Concept based Polymorphism是一种dynamic polymorphism,因为 struct concept_tstruct model: concept_t 采用的是OOP interface + template implementation。

Value Semantics Polymorphism

对于C++ dynamic polymorphic,我们的经典认知是: 只有通过reference semantic、OOP interface + subclass implementation才能够实现dynamic polymorphic,而通过上述source code可以看出,它实现了dynamic polymorphic,但是并没有使用:

1、reference semantic

2、OOP interface + subclass implementation

using document_t = vector<object_t>;

是value semantic,而不是reference semantic。

显然, 这就是"Value Semantics Polymorphism"的含义。

通过用法验证上述分析

下面通过main()中的使用来验证前面的分析。

int main()
{
    /**
     * 加入type 为 int的object
     */
    current(h).emplace_back(0);
    /**
     * 加入type 为 string的object
     */
    current(h).emplace_back(string("Hello!"));

    draw(current(h), cout, 0);
}    

下面是draw(current(h), cout, 0)的实现,它是"Value Semantics and Concept-based Polymorphism"发生的地方:

void draw(const document_t &x, ostream &out, size_t position)
{
    out << string(position, ' ') << "<document>" << endl;
    for (auto &e : x)
        draw(e, out, position + 2);
    out << string(position, ' ') << "</document>" << endl;
}
    /**
     * @brief 这里使用的是friend function,其实也可以修改为member function
     *
     * @param x
     * @param out
     * @param position
     */
    friend void draw(const object_t &x, ostream &out, size_t position)
    {
        /**
         * dynamic polymorphism
         */
        x.self_->draw_(out, position);
    }

1、上述 int object、std::string object之间没有inheritance关系

2、 int object、std::string object都是value semantic

3、实现了dynamic polymorphism

实现分析

有了type erasure的实现思路,再来看concept-based Polymorphism的实现就非常容易了。下面是它的实现中使用到的关键。

Type erasure

它的实现是运用了type erasure的技术的,这些technique都是在Type-erasure章节中总结过的,主要是:

1、template constructor

    /**
     * @brief template constructor
     *
     * @tparam T
     * @param x
     */
    template<typename T>
    object_t(T x) :
                    self_(make_shared<model<T>>(move(x)))
    {
    }

2、OOP interface + template implementation

    struct concept_t
    {
        virtual ~concept_t() = default;
        virtual void draw_(ostream&, size_t) const = 0;
    };
    template<typename T>
    struct model: concept_t
    {
        model(T x) :
                        data_(move(x))
        {
        }
        void draw_(ostream &out, size_t position) const
        {
            /**
             * static polymorphism: overload
             */
            draw(data_, out, position);
        }
        /**
         * 这里使用的是value,而不是pointer
         */
        T data_;
    };

External polymorphism and static polymorphism

1、使用overload、function template来实现static polymorphism,

2、此处强调 external polymorphism 的原因是,我觉得上述它的这种写法是非常 类似于 Adapter-pattern\External-polymorphism-pattern 章节中总结的实现方法的。

function template:

// Library
template<typename T>
void draw(const T &x, ostream &out, size_t position)
{
    out << string(position, ' ') << x << endl;
}

overload:

void draw(const my_class_t&, ostream &out, size_t position)
{
    out << string(position, ' ') << "my_class_t" << endl;
}