c++ api 디자인 - 확장성

27
C++ API Design Ch.12 확장성 Cecil

Upload: hyeonseok-choi

Post on 20-May-2015

413 views

Category:

Technology


5 download

TRANSCRIPT

Page 1: C++ API 디자인 - 확장성

C++����������� ������������������  API����������� ������������������  Design����������� ������������������  Ch.12����������� ������������������  확장성

Cecil

Page 2: C++ API 디자인 - 확장성

In����������� ������������������  this����������� ������������������  chapter

•Plugin����������� ������������������  

•상속����������� ������������������  

•템플릿

Page 3: C++ API 디자인 - 확장성

Plugin

Page 4: C++ API 디자인 - 확장성

플러그인을����������� ������������������  통한����������� ������������������  확장����������� ������������������  방법

12.1.1 Plugin Model OverviewMany examples of commercial software packages allow their core functionality to be extendedthrough the use of C/C++ plugins. For example, the Apache Web server supports C-based “modules,”Adobe Photoshop supports a range of plugin types to manipulate images, and Web browsers such asFirefly, Chrome, and Opera support the Netscape Plugin API (NPAPI) for the creation of browserplugins such as the Adobe Flash or PDF Reader plugins. The Qt toolkit can also be extended viathe QPluginLoader class. (A server-based plugin API such as Apache’s module interface is some-times referred to as a Server API, or SAPI.)

Some of the benefits of adopting a plugin model in your API are as follows.

• Greater versatility. Your API can be used to solve a greater range of problems, without requir-ing you to implement solutions for all of those problems.

• Community catalyst. By giving your users the ability to solve their own problems within the frame-work of your API, you can spark a community of user-contributed additions to your base design.

• Smaller updates. Functionality that exists as a plugin can be updated easily independently of theapplication by simply dropping in a new version of the plugin. This can often be a much smallerupdate than distributing a new version of the entire application.

• Future proofing. Your API may reach a level of stability where you feel that no further updatesare necessary. However, further evolution of the functionality of your API can continue throughthe development of plugins, allowing the API to maintain its usefulness and relevance for agreater period of time. For example, the NPAPI has changed little in recent years, but it is stilla popular method to write plugins for many Web browsers.

• Isolating risk. Plugins can be beneficial for in-house development too by letting engineerschange functionality without destabilizing the core of your system.

As was just hinted, a plugin system doesn’t have to be used only by your clients. You candevelop parts of your Core API implementation as plugins too. In fact, this is actually a good prac-tice because it ensures that you fully exercise your plugin architecture and that you live in the sameworld as your users (“eat your own dog food”). For example, the GNU Image Manipulation Program(GIMP) ships many of its built-in image processing functions as plugins using its GIMP Plugin API.

API Code

API

PLUGIN

API Library

Plugin Code Plugin Library

Compiler

Compiler

FIGURE 12.1

A plugin library is a dynamic library that can be compiled separately from a Core API and explicitly loaded bythe API on demand.

362 CHAPTER 12 Extensibility

핵심����������� ������������������  API와����������� ������������������  별개로����������� ������������������  컴파일����������� ������������������  되며,����������� ������������������  필요시����������� ������������������  로드되어����������� ������������������  사용

Page 5: C++ API 디자인 - 확장성

플러그인����������� ������������������  사용����������� ������������������  예:����������� ������������������  world����������� ������������������  of����������� ������������������  warcraft

Page 6: C++ API 디자인 - 확장성

플러그인����������� ������������������  사용시����������� ������������������  이점

다재다능한����������� ������������������  능력,����������� ������������������  커뮤니티����������� ������������������  활성화����������� ������������������  

업데이트����������� ������������������  간소화,미래����������� ������������������  경쟁력����������� ������������������  강화����������� ������������������  

리스크����������� ������������������  격리화

Page 7: C++ API 디자인 - 확장성

일반적인����������� ������������������  플러그인����������� ������������������  시스템����������� ������������������  구조

플러그인����������� ������������������  매니저:����������� ������������������  플러그인����������� ������������������  수명����������� ������������������  주기를����������� ������������������  관리하는����������� ������������������  API����������� ������������������  코드����������� ������������������  

플러그인����������� ������������������  API:����������� ������������������  플러그인을����������� ������������������  만들기����������� ������������������  위해서����������� ������������������  사용자가����������� ������������������  컴파일하는����������� ������������������  API

12.1.2 Plugin System Design IssuesThere are many different ways to design a plugin system. The best solution for your current projectmay not be the best choice for your next project. I will therefore start by trying to tease out some ofthe high-level issues that you should be aware of when devising a plugin system.

At the same time, there are also a number of general concepts applicable to all plugin systems.For example, when supporting dynamic library plugins, you will always need a mechanism to loada dynamic library and access symbols in that file. In general, when creating any plugin system, thereare two major features that you must design (see Figure 12.2).

1. The Plugin API: This is the API that your users must compile and link against in order to createa plugin. I differentiate this from your Core API, which is the larger code base into which you areadding the plugin system.

2. The Plugin Manager: This is an object (often a singleton) in the Core API code that manages thelife cycle of all plugins, that is, loading, registration, and unloading. This object can also becalled the Plugin Registry.

With these general concepts in hand, let’s take a look at some of the design decisions that willaffect the precise plugin architecture that you should build for your API.

• C versus C++. As discussed earlier in the versioning chapter, the C++ specification does notdefine a specific ABI. Therefore, different compilers, and even different versions of the samecompiler, can produce code that is binary incompatible. The implication for a plugin system isthat plugins developed by clients using a compiler with a different ABI may not be loadable.In contrast, the ABI for plain C code is well defined and will work across platforms andcompilers.

Plugin 1Core API

PluginManager Plugin 2

Plugin 2

FIGURE 12.2

The Plugin Manager lives in the Core API. It discovers and loads plugins that have been built against the PluginAPI.

364 CHAPTER 12 Extensibility

Page 8: C++ API 디자인 - 확장성

플러그인����������� ������������������  설계시����������� ������������������  고려사항����������� ������������������  In����������� ������������������  C++

C++����������� ������������������  vs����������� ������������������  C,����������� ������������������  버전����������� ������������������  관리����������� ������������������  

내부����������� ������������������  vs����������� ������������������  외부����������� ������������������  메타����������� ������������������  데이터����������� ������������������  

일반화����������� ������������������  vs����������� ������������������  특화된����������� ������������������  플러그인����������� ������������������  매니저����������� ������������������  

보안,����������� ������������������  정적����������� ������������������  vs����������� ������������������  동적����������� ������������������  라이브러리

Page 9: C++ API 디자인 - 확장성

C++로����������� ������������������  플러그인����������� ������������������  개발시����������� ������������������  이슈

플러그인����������� ������������������  개발자는����������� ������������������  API와����������� ������������������  같은����������� ������������������  컴파일러����������� ������������������  버전을����������� ������������������  사용해야����������� ������������������  함����������� ������������������  

or����������� ������������������  바인딩����������� ������������������  기술����������� ������������������  도입이����������� ������������������  필요(COM,����������� ������������������  IPC����������� ������������������  …⋯)

Page 10: C++ API 디자인 - 확장성

C++만����������� ������������������  사용해서����������� ������������������  해결하려면?

추상화����������� ������������������  베이스����������� ������������������  클래스����������� ������������������  사용����������� ������������������  

자유����������� ������������������  함수를����������� ������������������  위한����������� ������������������  C����������� ������������������  링크����������� ������������������  사용����������� ������������������  (extern)����������� ������������������  

STL����������� ������������������  및����������� ������������������  예외����������� ������������������  사용����������� ������������������  금지����������� ������������������  

혼합����������� ������������������  할당자����������� ������������������  사용����������� ������������������  금지

Page 11: C++ API 디자인 - 확장성

플러그인����������� ������������������  API����������� ������������������  개발����������� ������������������  방법

플러그인����������� ������������������  등록시����������� ������������������  콜백����������� ������������������  구조체를����������� ������������������  등록����������� ������������������  

or����������� ������������������  플러그인을����������� ������������������  실행하기����������� ������������������  위해����������� ������������������  호출하는����������� ������������������  함수의����������� ������������������  이름을����������� ������������������  fix

Page 12: C++ API 디자인 - 확장성

구조체����������� ������������������  Callback12.1.4 The Plugin APIThe Plugin API is the interface that you provide to your users to create plugins. I’ll call itpluginapi.h in our example here. This header file will contain functionality that allows pluginsto communicate with the Core API.

When the Core API loads a plugin, it needs to know which functions to call or symbols to accessin order to let the plugin do its work. This means that you should define specifically named entrypoints in the plugin that your users must provide. There are several different ways that you can dothis. For example, when writing a GIMP plugin, you must define a variable called PLUG IN INFO thatlists the various callbacks defined in the plugin.

#include <libgimp/gimp.h>

GimpPlugInInfo PLUG IN INFO{

NULL, /* called when GIMP starts */NULL, /* called when GIMP exits */query, /* procedure registration and arguments definition */run, /* perform the plugin’s operation */

};

Netscape Plugins use a similar, although slightly more flexible, technique. In this case, plugin wri-ters define an NP GetEntryPoints() function and fill in the appropriate fields of the NPPluginFuncs

structure that the browser passes in during plugin registration. The NPPluginFuncs structure includessize and version fields to handle future expansion.

Another solution is to have specifically named functions that the Core API can call, if they areexported by the plugin. I will adopt this approach for our example because it is simple and scalable;for example, it doesn’t rely on a fixed size array or structure.

The two most basic callbacks that a plugin should provide are an initialization function and a cleanupfunction. As noted earlier, these functions should be declared with C linkage to avoid name manglingdifferences between compilers. If you want to develop a cross-platform plugin system, you will also haveto deal with correctly using declspec(dllexport) and declspec(dllimport) decorators on Win-dows. Instead of requiring our plugin developers to know all of these details, I will provide some macrosto simplify everything. (As stated earlier, you should avoid preprocessor macros for declaring thingssuch as API constants; however, they are perfectly valid to affect compile-time configuration like this.)

Also, I’ve decided that our plugin should be allowed to register new IRenderer derived classes soI’ll provide a Plugin API call to let plugins do just this. Here’s a first draft of our Plugin API:

// pluginapi.h#include "defines.h"#include "renderer.h"

#define CORE FUNC extern "C" CORE API#define PLUGIN FUNC extern "C" PLUGIN API

#define PLUGIN INIT() PLUGIN FUNC int PluginInit()#define PLUGIN FREE() PLUGIN FUNC int PluginFree()

36712.1 Extending via plugins

플러그인����������� ������������������  등록시����������� ������������������  콜백����������� ������������������  구조체를����������� ������������������  등록

Page 13: C++ API 디자인 - 확장성

호출하는����������� ������������������  함수����������� ������������������  이름을����������� ������������������  fix

동적����������� ������������������  로딩시����������� ������������������  정해진����������� ������������������  심볼을����������� ������������������  찾아����������� ������������������  플러그인����������� ������������������  호출����������� ������������������  ex)����������� ������������������  PluginInit,����������� ������������������  PluginFree

// pluginapi.h

#include "defines.h"

#include "renderer.h"

#define CORE_FUNC extern "C" CORE_API

#define PLUGIN_FUNC extern "C" PLUGIN_API

#define PLUGIN_INIT() PLUGIN_FUNC int PluginInit()

#define PLUGIN_FREE() PLUGIN_FUNC int PluginFree()

typedef IRenderer *(*RendererInitFunc)();

typedef void (*RendererFreeFunc)(IRenderer *);

CORE_FUNC void RegisterRenderer(const char *type, RendererInitFunc init cb, RendererFreeFunc free cb);

Page 14: C++ API 디자인 - 확장성

플러그인����������� ������������������  예제����������� ������������������  코드// plugin1.cpp #include "pluginapi.h" #include <iostream> class OpenGLRenderer : public IRenderer { public: ~OpenGLRenderer() {} bool LoadScene(const char *filename) { return true; } void SetViewportSize(int w, int h) {} void SetCameraPosition(double x, double y, double z) {} void SetLookAt(double x, double y, double z) {} void Render() { std::cout << "OpenGL Render" << std::endl; }

};PLUGIN_FUNC IRenderer *CreateRenderer() { return new OpenGLRenderer(); }PLUGIN_FUNC void DestroyRenderer(IRenderer *r) { delete r; }PLUGIN_INIT() { RegisterRenderer("opengl", CreateRenderer, DestroyRenderer); return 0; }

// renderer.h #include <string> class IRenderer { public: virtual ~IRenderer() {} virtual bool LoadScene(const char *filename) = 0; virtual void SetViewportSize(int w, int h) = 0; virtual void SetCameraPosition(double x, double y, double z) = 0; virtual void SetLookAt(double x, double y, double z) = 0; virtual void Render() = 0; };

플러그인����������� ������������������  수정����������� ������������������  없이����������� ������������������  ����������� ������������������  

플러그인����������� ������������������  매니저����������� ������������������  수정을����������� ������������������  위해����������� ������������������  

별도의����������� ������������������  함수를����������� ������������������  제공

Page 15: C++ API 디자인 - 확장성

플러그인����������� ������������������  매니저����������� ������������������  코드// pluginmanager.cpp #include "defines.h" #include <string> #include <vector> class CORE API PluginInstance { public: explicit PluginInstance(const std::string &name); ~PluginInstance(); bool Load(); bool Unload(); bool IsLoaded(); std::string GetFileName(); std::string GetDisplayName(); private: PluginInstance(const PluginInstance &); const PluginInstance &operator (const PluginInstance &); class Impl; Impl *mImpl; };

class CORE_API PluginManager { public: static PluginManager &GetInstance(); bool LoadAll(); bool Load(const std::string &name); bool UnloadAll(); bool Unload(const std::string &name); std::vector<PluginInstance *> GetAllPlugins(); private: PluginManager(); ~PluginManager(); PluginManager(const PluginManager &); const PluginManager &operator (const PluginManager &); std::vector<PluginInstance *> mPlugins; };

메타����������� ������������������  데이터를����������� ������������������  관리하고,����������� ������������������  라이브러리를����������� ������������������  로드/언로드����������� ������������������  주로����������� ������������������  싱글톤으로����������� ������������������  구현됨

Page 16: C++ API 디자인 - 확장성

플러그인����������� ������������������  버전����������� ������������������  관리

핵심����������� ������������������  API����������� ������������������  버전과����������� ������������������  동일하게����������� ������������������  관리����������� ������������������  

or����������� ������������������  플러그인����������� ������������������  API����������� ������������������  버전을����������� ������������������  따로����������� ������������������  관리����������� ������������������  

사용자는����������� ������������������  플러그인이����������� ������������������  어떤����������� ������������������  버전과����������� ������������������  호환이����������� ������������������  되는지����������� ������������������  명시����������� ������������������  해야����������� ������������������  함.

Page 17: C++ API 디자인 - 확장성

상속

Page 18: C++ API 디자인 - 확장성

상속을����������� ������������������  통한����������� ������������������  확장

기능����������� ������������������  추가:����������� ������������������  클래스를����������� ������������������  상속����������� ������������������  받아����������� ������������������  하위����������� ������������������  클래스에서����������� ������������������  기능����������� ������������������  추가����������� ������������������  

베이스����������� ������������������  클래스가����������� ������������������  상속을����������� ������������������  염두에����������� ������������������  두고����������� ������������������  설계����������� ������������������  되어야����������� ������������������  함.����������� ������������������  (가상����������� ������������������  소멸자)����������� ������������������  

!

기능����������� ������������������  수정:����������� ������������������  하위����������� ������������������  클래스에서����������� ������������������  오버라이드하여����������� ������������������  기능을����������� ������������������  수정(templete����������� ������������������  method)

Page 19: C++ API 디자인 - 확장성

상속과����������� ������������������  STL

STL����������� ������������������  컨데이터����������� ������������������  클래스는����������� ������������������  가상����������� ������������������  소멸자를����������� ������������������  제공하지����������� ������������������  않음.����������� ������������������  

STL����������� ������������������  컨데이터����������� ������������������  클래스는����������� ������������������  상속����������� ������������������  받지����������� ������������������  않는����������� ������������������  것이����������� ������������������  규칙

Page 20: C++ API 디자인 - 확장성

Visitorof methods that operate on every node in a scene graph hierarchy or to traverse the derivation tree ofa programming language parser and output a human-readable form of the program.

Let’s develop a visitor example to illustrate how this pattern works. I’ll use the example of a scenegraph hierarchy that describes a 3D scene, such as that used by Open Inventor, OpenSceneGraph, orthe Virtual Reality Modeling Language. To keep the example simple, our scene graph will contain onlythree different node types: Shape, Transform, and Light. Figure 12.4 shows an example hierarchyusing these node types.

I’ll begin by defining our abstract visitor interface. Clients can create concrete subclasses of thisinterface in order to add custom operations to the scene graph. It essentially declares a Visit()

method for each node type in the scene graph.

// nodevisitor.hclass ShapeNode;class TransformNode;class LightNode;

class INodeVisitor{public:

virtual ~INodeVisitor() {}

Transform0

Transform1 Transform2

Shape0 Shape1 Shape2

Light0

FIGURE 12.4

Example scene graph hierarchy showing nodes of different types.

37912.2 Extending via inheritance

이전의����������� ������������������  데이터����������� ������������������  구조에����������� ������������������  변경����������� ������������������  없이����������� ������������������  새로운����������� ������������������  기능����������� ������������������  추가가����������� ������������������  가능

{public:

SceneGraph();!SceneGraph();void Traverse(INodeVisitor &visitor);

private:SceneGraph(const SceneGraph &);const SceneGraph &operator =(const SceneGraph &);class Impl;Impl *mImpl;

};

Note that each of the node types declares an Accept() method, taking a visitor object as itsparameter. This method is used to call the appropriate Visit() method in the visitor class. Thiscan be thought of as a way to have a single virtual method in each node that can then call anyuser-supplied virtual method. See Figure 12.5 for a UML diagram that shows this Visitor pattern.

// scenegraph.cppvoid ShapeNode::Accept(INodeVisitor &visitor){

visitor.Visit(*this);}

<< interface >> << interface >>BaseNode

NodeA NodeB

Visitor

+ Accept(visitor: Visitor) : void

+ Accept(visitor: Visitor) : void+ Accept(visitor: Visitor) : void

+ Visit(node: NodeA) : void+ Visit(node: NodeB) : void

FIGURE 12.5

UML diagram of the Visitor design pattern.

38112.2 Extending via inheritance

Page 21: C++ API 디자인 - 확장성

Visitor����������� ������������������  Code// scenegraph.h #include <string> class INodeVisitor; class BaseNode { public: explicit BaseNode(const std::string &name); virtual ~BaseNode() {} virtual void Accept(INodeVisitor &visitor) = 0; private: std::string mName; }; class ShapeNode : public BaseNode { public: explicit ShapeNode(const std::string &name); void Accept(INodeVisitor &visitor); }; class TransformNode : public BaseNode { public: explicit TransformNode(const std::string &name); void Accept(INodeVisitor &visitor); }; !// scenegraph.cpp void ShapeNode::Accept(INodeVisitor &visitor) { visitor.Visit(*this); } void TransformNode::Accept(INodeVisitor &visitor) { visitor.Visit(*this); }

// nodevisitor.h

class ShapeNode;

class TransformNode;

class LightNode;

class INodeVisitor {

public:

virtual ~INodeVisitor() {}

virtual void Visit(ShapeNode &node) = 0;

virtual void Visit(TransformNode &node) = 0;

virtual void Visit(LightNode &node) = 0;

};

visitor에����������� ������������������  따라����������� ������������������  다른����������� ������������������  기능을����������� ������������������  수행

Page 22: C++ API 디자인 - 확장성

Visitor����������� ������������������  패턴의����������� ������������������  특징

데이터����������� ������������������  구조의����������� ������������������  수정����������� ������������������  없이����������� ������������������  새로운����������� ������������������  기능����������� ������������������  추가����������� ������������������  가능.����������� ������������������  

일관된����������� ������������������  연산을����������� ������������������  수행하는����������� ������������������  코드를����������� ������������������  한����������� ������������������  곳에����������� ������������������  위치����������� ������������������  시킬수����������� ������������������  있음.����������� ������������������  

But,����������� ������������������  새로운����������� ������������������  노드의����������� ������������������  추가는����������� ������������������  전체����������� ������������������  visitor의����������� ������������������  변경을����������� ������������������  필요.

Page 23: C++ API 디자인 - 확장성

상속을����������� ������������������  제한하는����������� ������������������  방법����������� ������������������  in����������� ������������������  c++

생성자를����������� ������������������  private로����������� ������������������  선언����������� ������������������  

(클래스����������� ������������������  인스턴스를����������� ������������������  stack에����������� ������������������  생성����������� ������������������  불가)����������� ������������������  

!

가상����������� ������������������  상속을����������� ������������������  이용한����������� ������������������  방법

class NonBaseFinal { private: NonBaseFinal() {} friend class NonBase; };

class NonBase :virtual public NonBaseFinal { … };

Page 24: C++ API 디자인 - 확장성

템플릿

Page 25: C++ API 디자인 - 확장성

정책����������� ������������������  기반����������� ������������������  템플릿

정책이라����������� ������������������  불리는����������� ������������������  작은����������� ������������������  클래스를����������� ������������������  통한����������� ������������������  복잡한����������� ������������������  행동을����������� ������������������  구현하는����������� ������������������  접근법

template< typename T, template <class> class OwnershipPolicy = RefCounted, class ConversionPolicy = DisallowConversion, template <class> class CheckingPolicy AssertCheck, template <class> class StoragePolicy DefaultSPStorage > class SmartPtr;

각각은����������� ������������������  다양한����������� ������������������  정책을����������� ������������������  명시

많은����������� ������������������  수의����������� ������������������  템플릿����������� ������������������  파라미터를����������� ������������������  통해����������� ������������������  코드를����������� ������������������  작성하는����������� ������������������  일은����������� ������������������  쉽지����������� ������������������  않음.

Page 26: C++ API 디자인 - 확장성

정교하게����������� ������������������  반복되는����������� ������������������  템플릿

template <typename T> class Base;

class Derived : public Base<Derived>;

베이스����������� ������������������  클래스가����������� ������������������  파생����������� ������������������  클래스의����������� ������������������  네임스페스에����������� ������������������  접근����������� ������������������  가능

런타임시����������� ������������������  가상����������� ������������������  메서드를����������� ������������������  호출할때����������� ������������������  발생하는����������� ������������������  오버헤드를����������� ������������������  피할수����������� ������������������  있음.

Page 27: C++ API 디자인 - 확장성

References

•Martin����������� ������������������  Reddy����������� ������������������  (2014).����������� ������������������  C++����������� ������������������  API����������� ������������������  디자인.����������� ������������������  (천호민,����������� ������������������  옮김).����������� ������������������  고양시:����������� ������������������  지앤선.����������� ������������������  (원서출판����������� ������������������  2013)