The design of application programming interfaces can affect the behavior, capabilities, stability, and ease of use of end-user applications. With this book, you will learn how to design a good API for large-scale long-term projects. With extensive C++ code to illustrate each concept, API Design for C++ covers all of the strategies of world-class API development. Martin Reddy ...
The design of application programming interfaces can affect the behavior, capabilities, stability, and ease of use of end-user applications. With this book, you will learn how to design a good API for large-scale long-term projects. With extensive C++ code to illustrate each concept, API Design for C++ covers all of the strategies of world-class API development. Martin Reddy draws on over fifteen years of experience in the software industry to offer in-depth discussions of interface design, documentation, testing, and the advanced topics of scripting and plug-in extensibility. Throughout, he focuses on various API styles and patterns that will allow you to produce elegant and durable libraries.
The only book that teaches the strategies of C++ API development, including design, versioning, documentation, testing, scripting, and extensibility. Extensive code examples illustrate each concept, with fully functional examples and working source code for experimentation available online. Covers various API styles and patterns with a focus on practical and efficient designs for large-scale long-term projects.
作者简介
· · · · · ·
作者简介:
Martin Reddy
博士是软件行业的一名老兵,有着15年以上的从业经验,共撰写过40多篇论文,拥有3项软件专利,并与他人合著了Level of Detail for 3D Graphics。另外,他还是ACM以及IEEE的会员。
When using the pimpl idiom use a private nested implementation class. Only use a public nested Impl class (or a public non-nested class) if other classes or free functions in the .cpp must access Impl members.
1. You can’t hide private virtual methods in the implementation class. These must appear in the public class so that any derived classes are able to override them.
2. You may need to add a pointer in the implementation class back to the public class so that the Impl class can call public methods. Although you could also pass the public class into the implementation class methods that need it. (查看原文)
Use explicit size-based types. The size of various types can differ by platform, compiler, and whether you are building a 32-bit or a 64-bit application. If you want to specify the exact size of a member variable, then you should use a type that specifically enforces this rather than assuming that types such as bool, short, or int will be a specific size. Unfortunately, the way to declare a fix...
2012-02-06 16:13:18
Use explicit size-based types.
The size of various types can differ by platform, compiler, and
whether you are building a 32-bit or a 64-bit application. If you want to specify the exact size of a member variable, then you should use a type that specifically enforces this rather than assuming that types such as bool, short, or int will be a specific size. Unfortunately, the way to declare a fixed-size variable varies for different platforms. For example, on UNIX-based systems, the stdint.h header file provides types such as int8 t, uint32 t, and int64 t to specify an 8-bit integer, 32-bit unsigned integer, and a 64-bit integer, respectively. However, the Boost library provides platform-independent versions of these types in the boost/cstdint.hpp header.
TIP
Use size specific types, such as int32 t or uint16 t, to specify the maximum number of required bits for a variable.
------------------------------------------------------
7.8.2 Random Access
1. The [] operator. This is meant to simulate the array indexing syntax of C/C++. Normally this operator is implemented without any bounds checking so that it can be made very efficient.
2. The at() method. This method is required to check if the supplied index is out of range and throw an exception in this case. As a result, this approach can be slower than the [] operator.
------------------------------------------------------
P253
8.4.4 Binary Compatibility
Binary-Incompatible API Changes:
* Removing a class, method, or function.
* Adding, removing, or reordering member variables for a class.
* Adding or removing base classes from a class.
* Changing the type of any member variable.
* Changing the signature of an existing method in any way.
* Adding, removing, or reordering template arguments.
* Changing a non-inlined method to be inlined.
* Changing a non-virtual method to be virtual, and vice versa.
* Changing the order of virtual methods.
* Adding a virtual method to a class with no existing virtual methods.
* Adding new virtual methods (some compilers may preserve binary compatibility if you only add new virtual methods after existing ones).
* Overriding an existing virtual method (this may be possible in some cases, but is best avoided).
Binary-Compatible API Changes:
* Adding new classes, non-virtual methods, or free functions.
* Adding new static variables to a class.
* Removing private static variables (if they are never referenced from an inline method).
* Removing non-virtual private methods (if they are never called from an inline method).
* Changing the implementation of an inline method (however, this requires recompilation to pick up the new implementation).
* Changing an inline method to be non-inline (however, this requires recompilation if the implementation is also changed).
* Changing the default arguments of a method (however, this requires recompilation to actually use the new default argument).
* Adding or removing friend declarations from a class.
* Adding a new enum to a class.
* Appending new enumerations to an existing enum.
* Using unclaimed remaining bits of a bit field.
--------------------------------------------------------------------
P259
8.5.3 Deprecating Functionality
// deprecated.h
#ifdef GNUC
# define DEPRECATED attribute ((deprecated))
#elif defined( MSC VER)
# define DEPRECATED declspec(deprecated)
#else
# define DEPRECATED
# pragma message("DEPRECATED is not defined for this compiler")
#endif
Using this definition, you can mark certain methods as being deprecated in the following way:
#include "deprecated.h"
#include <string>
class MyClass
{
public:
DEPRECATED std::string GetName();
std::string GetFullName();
};
CH 6.4.3 Explicit Instantiation API Design If you want to provide only a predetermined set of template specializations for your API and disallow your users from creating further ones, then you do in fact have the option of completely hiding your private code. In this case, you can put your template definitions into a .cpp file and use explicit template instantiation to instantiate those special...
2012-02-03 14:01:03
CH 6.4.3 Explicit Instantiation API Design
If you want to provide only a predetermined set of template specializations for your API and disallow your users from creating further ones, then you do in fact have the option of completely hiding your private code.
In this case, you can put your template definitions into a .cpp file and use explicit template instantiation to instantiate those specializations that you wish to export as part of your API.
// stack.h
#ifndef STACK H
#define STACK H
#include <vector>
template <typename T>
class Stack
{
public:
void Push(T val);
T Pop();
bool IsEmpty() const;
private:
std::vector<T> mStack;
};
#endif
// stack.cpp
#include "stack.h"
#include <string>
template <typename T>
void Stack<T>::Push(T val)
{
mStack.push back(val);
}
template <typename T>
T Stack<T>::Pop()
{
if (IsEmpty())
return T();
T val mStack.back();
mStack.pop back();
return val;
}
template <typename T>
bool Stack<T>::IsEmpty() const
{
return mStack.empty();
}
// explicit template instantiations
template class Stack<int>;
template class Stack<double>;
template class Stack<std::string>;
The important lines here are the last three, which create explicit instantiations of the Stack class template for the types int, double, and std::string. The user will not be able to create further specializations (and the compiler will not be able to create implicit instantiations for the user either) because the implementation details are hidden in our .cpp file. However, our implementation details are now hidden successfully in our .cpp file.
To indicate to your users which template specializations they can use (i.e., which ones you have explicitly instantiated for them), you could add a few typedefs to the end of your public header, such as
typedef Stack<int> IntStack;
typedef Stack<double> DoubleStack;
typedef Stack<std::string> StringStack;
It’s worth noting that by adopting this template style, not only do you (and your clients) get faster builds due to the removal of the overhead of implicit instantiation, but also, by removing the template definitions from your header, you reduce the #include coupling of your API and reduce the amount of extra code that your clients’ programs must compile every time they #include your API headers.
CH4.7 Function Design 4.7.4 Error Handling P148 By comparison, exceptions let your clients separate their error handling code from the normal flow of control, making for more readable code. They offer the benefit of being able to catch one or more errors in a sequence of several function calls, without having to check every single return code, and they let you handle an error higher up in the c...
2012-02-02 14:08:15
CH4.7 Function Design
4.7.4 Error Handling
P148
By comparison, exceptions let your clients separate their error handling code from the normal
flow of control, making for more readable code. They offer the benefit of being able to catch one
or more errors in a sequence of several function calls, without having to check every single return
code, and they let you handle an error higher up in the call stack instead of at the exact point of failure.
An exception can also carry more information than a simple error code.引自第148页
P149
Typically the use of exceptions
is an all-or-nothing proposition, meaning that if any part of an application uses exceptions then
the entire application must be prepared to handle exceptions correctly. This means that the use of
exceptions in your API also requires your clients to write exception safe code. It’s noteworthy that
Google forbids the use of exceptions in their C++ coding conventions because most of their existing
code is not tolerant of exceptions.引自第148页
CH 6.4.3 Explicit Instantiation API Design If you want to provide only a predetermined set of template specializations for your API and disallow your users from creating further ones, then you do in fact have the option of completely hiding your private code. In this case, you can put your template definitions into a .cpp file and use explicit template instantiation to instantiate those special...
2012-02-03 14:01:03
CH 6.4.3 Explicit Instantiation API Design
If you want to provide only a predetermined set of template specializations for your API and disallow your users from creating further ones, then you do in fact have the option of completely hiding your private code.
In this case, you can put your template definitions into a .cpp file and use explicit template instantiation to instantiate those specializations that you wish to export as part of your API.
// stack.h
#ifndef STACK H
#define STACK H
#include <vector>
template <typename T>
class Stack
{
public:
void Push(T val);
T Pop();
bool IsEmpty() const;
private:
std::vector<T> mStack;
};
#endif
// stack.cpp
#include "stack.h"
#include <string>
template <typename T>
void Stack<T>::Push(T val)
{
mStack.push back(val);
}
template <typename T>
T Stack<T>::Pop()
{
if (IsEmpty())
return T();
T val mStack.back();
mStack.pop back();
return val;
}
template <typename T>
bool Stack<T>::IsEmpty() const
{
return mStack.empty();
}
// explicit template instantiations
template class Stack<int>;
template class Stack<double>;
template class Stack<std::string>;
The important lines here are the last three, which create explicit instantiations of the Stack class template for the types int, double, and std::string. The user will not be able to create further specializations (and the compiler will not be able to create implicit instantiations for the user either) because the implementation details are hidden in our .cpp file. However, our implementation details are now hidden successfully in our .cpp file.
To indicate to your users which template specializations they can use (i.e., which ones you have explicitly instantiated for them), you could add a few typedefs to the end of your public header, such as
typedef Stack<int> IntStack;
typedef Stack<double> DoubleStack;
typedef Stack<std::string> StringStack;
It’s worth noting that by adopting this template style, not only do you (and your clients) get faster builds due to the removal of the overhead of implicit instantiation, but also, by removing the template definitions from your header, you reduce the #include coupling of your API and reduce the amount of extra code that your clients’ programs must compile every time they #include your API headers.
Use explicit size-based types. The size of various types can differ by platform, compiler, and whether you are building a 32-bit or a 64-bit application. If you want to specify the exact size of a member variable, then you should use a type that specifically enforces this rather than assuming that types such as bool, short, or int will be a specific size. Unfortunately, the way to declare a fix...
2012-02-06 16:13:18
Use explicit size-based types.
The size of various types can differ by platform, compiler, and
whether you are building a 32-bit or a 64-bit application. If you want to specify the exact size of a member variable, then you should use a type that specifically enforces this rather than assuming that types such as bool, short, or int will be a specific size. Unfortunately, the way to declare a fixed-size variable varies for different platforms. For example, on UNIX-based systems, the stdint.h header file provides types such as int8 t, uint32 t, and int64 t to specify an 8-bit integer, 32-bit unsigned integer, and a 64-bit integer, respectively. However, the Boost library provides platform-independent versions of these types in the boost/cstdint.hpp header.
TIP
Use size specific types, such as int32 t or uint16 t, to specify the maximum number of required bits for a variable.
------------------------------------------------------
7.8.2 Random Access
1. The [] operator. This is meant to simulate the array indexing syntax of C/C++. Normally this operator is implemented without any bounds checking so that it can be made very efficient.
2. The at() method. This method is required to check if the supplied index is out of range and throw an exception in this case. As a result, this approach can be slower than the [] operator.
------------------------------------------------------
P253
8.4.4 Binary Compatibility
Binary-Incompatible API Changes:
* Removing a class, method, or function.
* Adding, removing, or reordering member variables for a class.
* Adding or removing base classes from a class.
* Changing the type of any member variable.
* Changing the signature of an existing method in any way.
* Adding, removing, or reordering template arguments.
* Changing a non-inlined method to be inlined.
* Changing a non-virtual method to be virtual, and vice versa.
* Changing the order of virtual methods.
* Adding a virtual method to a class with no existing virtual methods.
* Adding new virtual methods (some compilers may preserve binary compatibility if you only add new virtual methods after existing ones).
* Overriding an existing virtual method (this may be possible in some cases, but is best avoided).
Binary-Compatible API Changes:
* Adding new classes, non-virtual methods, or free functions.
* Adding new static variables to a class.
* Removing private static variables (if they are never referenced from an inline method).
* Removing non-virtual private methods (if they are never called from an inline method).
* Changing the implementation of an inline method (however, this requires recompilation to pick up the new implementation).
* Changing an inline method to be non-inline (however, this requires recompilation if the implementation is also changed).
* Changing the default arguments of a method (however, this requires recompilation to actually use the new default argument).
* Adding or removing friend declarations from a class.
* Adding a new enum to a class.
* Appending new enumerations to an existing enum.
* Using unclaimed remaining bits of a bit field.
--------------------------------------------------------------------
P259
8.5.3 Deprecating Functionality
// deprecated.h
#ifdef GNUC
# define DEPRECATED attribute ((deprecated))
#elif defined( MSC VER)
# define DEPRECATED declspec(deprecated)
#else
# define DEPRECATED
# pragma message("DEPRECATED is not defined for this compiler")
#endif
Using this definition, you can mark certain methods as being deprecated in the following way:
#include "deprecated.h"
#include <string>
class MyClass
{
public:
DEPRECATED std::string GetName();
std::string GetFullName();
};
CH 6.4.3 Explicit Instantiation API Design If you want to provide only a predetermined set of template specializations for your API and disallow your users from creating further ones, then you do in fact have the option of completely hiding your private code. In this case, you can put your template definitions into a .cpp file and use explicit template instantiation to instantiate those special...
2012-02-03 14:01:03
CH 6.4.3 Explicit Instantiation API Design
If you want to provide only a predetermined set of template specializations for your API and disallow your users from creating further ones, then you do in fact have the option of completely hiding your private code.
In this case, you can put your template definitions into a .cpp file and use explicit template instantiation to instantiate those specializations that you wish to export as part of your API.
// stack.h
#ifndef STACK H
#define STACK H
#include <vector>
template <typename T>
class Stack
{
public:
void Push(T val);
T Pop();
bool IsEmpty() const;
private:
std::vector<T> mStack;
};
#endif
// stack.cpp
#include "stack.h"
#include <string>
template <typename T>
void Stack<T>::Push(T val)
{
mStack.push back(val);
}
template <typename T>
T Stack<T>::Pop()
{
if (IsEmpty())
return T();
T val mStack.back();
mStack.pop back();
return val;
}
template <typename T>
bool Stack<T>::IsEmpty() const
{
return mStack.empty();
}
// explicit template instantiations
template class Stack<int>;
template class Stack<double>;
template class Stack<std::string>;
The important lines here are the last three, which create explicit instantiations of the Stack class template for the types int, double, and std::string. The user will not be able to create further specializations (and the compiler will not be able to create implicit instantiations for the user either) because the implementation details are hidden in our .cpp file. However, our implementation details are now hidden successfully in our .cpp file.
To indicate to your users which template specializations they can use (i.e., which ones you have explicitly instantiated for them), you could add a few typedefs to the end of your public header, such as
typedef Stack<int> IntStack;
typedef Stack<double> DoubleStack;
typedef Stack<std::string> StringStack;
It’s worth noting that by adopting this template style, not only do you (and your clients) get faster builds due to the removal of the overhead of implicit instantiation, but also, by removing the template definitions from your header, you reduce the #include coupling of your API and reduce the amount of extra code that your clients’ programs must compile every time they #include your API headers.
CH4.7 Function Design 4.7.4 Error Handling P148 By comparison, exceptions let your clients separate their error handling code from the normal flow of control, making for more readable code. They offer the benefit of being able to catch one or more errors in a sequence of several function calls, without having to check every single return code, and they let you handle an error higher up in the c...
2012-02-02 14:08:15
CH4.7 Function Design
4.7.4 Error Handling
P148
By comparison, exceptions let your clients separate their error handling code from the normal
flow of control, making for more readable code. They offer the benefit of being able to catch one
or more errors in a sequence of several function calls, without having to check every single return
code, and they let you handle an error higher up in the call stack instead of at the exact point of failure.
An exception can also carry more information than a simple error code.引自第148页
P149
Typically the use of exceptions
is an all-or-nothing proposition, meaning that if any part of an application uses exceptions then
the entire application must be prepared to handle exceptions correctly. This means that the use of
exceptions in your API also requires your clients to write exception safe code. It’s noteworthy that
Google forbids the use of exceptions in their C++ coding conventions because most of their existing
code is not tolerant of exceptions.引自第148页
0 有用 王晓辰 2012-06-23 16:10:29
跳着读的
0 有用 微子 2011-05-03 12:02:10
接口要隐藏, 要小而正交.
0 有用 十二晨 2014-06-12 00:17:05
c++中级经典。
0 有用 死亡的飞翔 2019-04-12 16:44:30
讲的主题太多,但每个主题都只浅尝则止,大多数东西都已经有所了解,没有太多新的收获。
0 有用 tangboyun 2011-06-18 20:41:05
其实内容和《大规模C++程序设计》蛮接近的,强调了文档和给出了脚本Binding的一些例子,蛮不错的书,值得一看。
0 有用 Galois 2019-10-11 00:02:42
全面,未免泛泛而谈,而且一些内容已过时。
0 有用 死亡的飞翔 2019-04-12 16:44:30
讲的主题太多,但每个主题都只浅尝则止,大多数东西都已经有所了解,没有太多新的收获。
0 有用 Gearslogy 2017-12-31 22:42:45
这本书竟然写的这么好。。。。。。
1 有用 oyquan 2016-12-24 19:52:16
如果已经读过Effective C++、Exceptional C++等经典C++书籍,了解常用设计模式,那么你不会从这本书中读到太多你没见过的关于API设计的建议。 本书的最大优点是,对C++ API设计的最佳实践进行了总结,而且内容组织得很好,方便查阅,可以当做参考书使用。 为什么大家对它的评价不高呢?
1 有用 夏日晴空 2016-03-03 23:23:09
里面的设计模式例子非常好