我当前的项目涉及编写 C++ API,我决定使用 PIMPL 惯用语。
我应该在我的项目中的任何地方使用 PIMPL idiom,例如我需要创建一个继承自 的自定义类,std::exception我应该用 PIMPL idiom 设计这个类还是我可以只写一个公共实现?
仅仅因为我使用 PIMPL 习惯用法就认为我创建的每个类都应该围绕它设计,这感觉是错误的。是否有应该在哪里PIMPL任何异常,没有用?
std::unique<B>我为incomplete type创建了一个小测试用例B。
测试.h
#pragma once
#include <memory>
class B; //<--- compile error here
class Test{
std::unique_ptr<B> bPtr;
//#1 need to move destructor's implementation to .cpp
public: ~Test();
};
Run Code Online (Sandbox Code Playgroud)
测试.cpp
#include "Test.h"
class B{};
Test::~Test(){} //move here because it need complete type of B
Run Code Online (Sandbox Code Playgroud)
主程序
#include <iostream>
#include "Test.h"
using namespace std;
int main(){
Test test;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
我收到此错误:-
/usr/include/c++/4.8.2/bits/unique_ptr.h:65:22:错误:“sizeof”对不完整类型“B”的无效应用
据我了解,编译器告诉我这 B是一个不完整的类型(in main.cpp),因此它无法B正确删除。
但是,在我的设计中,我不想main.cpp拥有完整的B.
粗略地说,这是一个粉刺。
有没有好的解决方法?
这里有一些类似的问题,但没有一个提出干净的解决方法。 …
我正在编写一个库和一个客户端应用程序。在库中,我正在尝试围绕另一个静态链接的第三方库(特别是spdlog)编写一个包装器,并尝试使用 pImpl 习惯用法将它完全隐藏在客户端应用程序之外。问题是第三方库使用了可变参数模板函数,所以我也需要在我的库中使用。
我对包装器的第一次尝试非常简单和直接,但后来我在我的客户端应用程序中收到“没有这样的文件或目录”错误,因为第三方标头包含在我的库标头中。
我接下来尝试创建一个 pImpl 类并编译它,但在客户端我再次收到“未定义引用”链接器错误。
将实现的源代码拉到我的包装器的标题中,让我回到最初的“没有这样的文件”问题。对此进行研究后,我开始认为不可能围绕可变参数模板制作包装器,但我不确定。这是我第一次尝试制作可变参数函数/模板。
这是我的项目目前的情况:
为了简洁和清晰,几乎所有的命名空间、函数名称、标题等都已被编辑(或删除)。
#include "sandbox.h"
#include <logger.h> // <-- This is all I want clients to see.
int Sandbox::run() {
LOG_INFO("Hello World!"); // My library is providing this.
LOG_INFO("Hello {}", "indeed!"); // And, this variable input function.
return 0;
}
Run Code Online (Sandbox Code Playgroud)
class LoggerImp; // Forward declaration of implementation.
class LIB_EXPORT Logger {
public:
/* Constructors, destructor, etc. */
template <typename... Args>
void info(const …Run Code Online (Sandbox Code Playgroud) 我对我的库的公共 API 中的类使用 pimpl 习惯用法,以受益于它的 ABI 稳定性等属性。在我的非公开代码中,访问 impl 对象并直接对其进行操作会很方便。
向 API 类添加 getter 以返回 impl 指针是否被认为是不好的做法?为什么?
我的库的客户端无论如何都无法使用 impl obj,因为它的接口是非公开的。另一方面,我的库内部代码现在可以在 impl obj 上进行操作。
公共.h:
class PublicClass{
struct Impl;
Impl* m_impl;
public:
Impl* getImpl();
};
Run Code Online (Sandbox Code Playgroud)
实现.h:
struct Impl{
int foo();
};
Run Code Online (Sandbox Code Playgroud)
主要.cpp:
#include "public.h"
#include "impl.h"
int main(){
PublicClass p;
auto pimpl = p.getImpl();
auto interestingVal = pimpl->foo();
}
Run Code Online (Sandbox Code Playgroud)
只是一个设计问题。
标题可能有点太短,不够清晰。
我们有一个复杂的 C/C++ 项目,它是在许多单独的目标中作为静态库构建和链接的。所以我的问题是 target_link_libraries 进行传递链接,但顺序错误。我需要另一个订单,因为我们使用 PIMPL 并且实现没有任何要包含的标头。它们只包含定义 PIMPL 标头并声明 Impl 对象的库的标头。但为了正确链接,它们需要链接在为 Pimpl 提供标头的 lib 之后。这不会发生。
仅当我们使用用于单元测试框架的 gcc 编译器和链接器进行构建时,才会出现这种情况。对于我们的目标项目,我们使用 TI clang-toolchain,它提供了链接器选项 --reread_libs,可以自动解决此问题。据我所知,这在 gcc 中不可用。
现在更详细地介绍一下 Cmake。该项目太大,无法做一个最小的例子,但我会尝试描述问题并将其缩小为问题:
所以我们在一个模块中有多个静态库:
# static libs inside the platform
add_library(memutils memutils/ForwardDeclaredStorage.hpp)
target_include_directories(memutils INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/memutils)
set_target_properties(memutils PROPERTIES LINKER_LANGUAGE CXX)
# contains the Pimpl-Headers to use, here task and mutex as extract
add_library(osal osal/mutex.hpp osal/task.hpp osal/TaskManager.hpp osal/TaskManager.cpp)
target_include_directories(osal PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/osal)
target_link_libraries(osal PUBLIC memutils)
add_library(system sys/system.hpp sys/system.cpp)
target_link_libraries(system PUBLIC osal)
target_include_directories(system PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/sys)
add_library(platformlib INTERFACE)
# example with INTERFACE …Run Code Online (Sandbox Code Playgroud) boost :: shared_ptr真让我烦恼.当然,我理解这种事情的实用性,但我希望我可以使用它shared_ptr<A> 作为一个A*.请考虑以下代码
class A
{
public:
A() {}
A(int x) {mX = x;}
virtual void setX(int x) {mX = x;}
virtual int getX() const {return mX;}
private:
int mX;
};
class HelpfulContainer
{
public:
//Don't worry, I'll manager the memory from here.
void eventHorizon(A*& a)
{
cout << "It's too late to save it now!" << endl;
delete a;
a = NULL;
}
};
int main()
{
HelpfulContainer helpfulContainer;
A* a1 = new A(1); …Run Code Online (Sandbox Code Playgroud) 我正在使用pimpl习语实现几个类,并且遇到了一些设计问题.
首先,我总是看到像这样的pimpl
class Object
{
public:
Visible();
~Visible();
.. etc ..
private:
class ObjectImpl *_pimpl;
};
Run Code Online (Sandbox Code Playgroud)
我有几个类使用这种方法,我的问题是这些类中的几个需要访问彼此的实现细节,但_pimpl指针是私有的delcared.
任何人都可以看到声明_pimpl公开的缺点.显然,如果它是公开的,那么有人可能会意外(或故意)重新分配它.(我忽略了这样一个事实,即"私人"可能被#defined定义为"公共"并且无论如何都会授予访问权限.如果你这样做,那么你应该得到你得到的东西).
我很欣赏我的设计可能存在缺陷,并欢迎任何评论.
我真的不喜欢使用朋友,我不确定他们甚至会帮忙,因为你不能在没有完全定义Object的情况下转发声明Object :: ObjectImpl.
即
...
private:
class ObjectImpl *_pimpl;
friend class OtherObject::OtherObjectImpl; // this needs a fully qualified OtherObject
};
Run Code Online (Sandbox Code Playgroud)
谢谢马克.
*更新 - 更多细节**
我有两个类,一个叫做Command,另一个叫做Results.我在Command上有方法返回结果向量.
命令和结果都使用了pimpl习语.我希望结果的界面尽可能小.
class Command
{
public:
void getResults( std::vector< Results > & results );
void prepareResults( std::vector< Results > & results );
private:
class CommandImpl *_pimpl;
};
class Results
{
public:
class ResultsImpl;
Results( ResultsImpl * pimpl …Run Code Online (Sandbox Code Playgroud) 我的理解是,pimpl习惯用法的主要好处是隐藏实现文件中的数据成员而不是标题.但是,模板需要在头文件中完全定义,以便编译器按需实例化它们.在这种情况下,使用pimpl习惯用于模板化课程是否有任何优势?
考虑一下我正在写一个静态库.让它有一个班级Foo
// mylib.h
#include <dependency_header_from_other_static_library.h>
class Foo {
// ...
private:
type_from_dependent_library x;
}
Run Code Online (Sandbox Code Playgroud)
正如您所看到的,这个库(让它调用它mylib)取决于另一个库.它汇编得很好.但是当用户编译它的代码(使用Foo和包含mylib.h)并与我的lib链接时,编译失败,因为用户也需要有dependency_header_from_other_static_library.h头文件来编译代码.
我想隐藏用户的这种依赖.怎么做到这一点?想到的一件事是PIMPL成语.喜欢:
// mylib.h
#include <dependency_header_from_other_static_library.h>
class Foo {
// ...
private:
class FooImpl;
boost::shared_ptr<FooImpl> impl_;
}
// mylib_priv.h
class FooImpl {
// ...
private:
type_from_dependent_library x;
}
Run Code Online (Sandbox Code Playgroud)
但是,它需要我来复制类的接口Foo在FooImpl.而且,PIMPL在我的案例中使用是否过度杀伤?
谢谢.
D拥有一个出色的模块系统,与C++相比,它可以大大缩短编译时间.根据文档,D仍然提供不透明的结构和联合,以便启用pimpl习语.我的问题是:如何在一个模块中声明嵌套结构(或联合)并在另一个模块中定义它?那是什么语法?
在C++中,标题看起来像这样
struct S {
...
struct Impl;
Impl * p;
};
Run Code Online (Sandbox Code Playgroud)
并且实现文件(cpp-file)将使用一些有趣的::-syntax,如下所示:
#include "header.h"
struct S::Impl {
...
};
Run Code Online (Sandbox Code Playgroud)
如何在D中实现相同的功能?
pimpl-idiom ×10
c++ ×9
shared-ptr ×2
templates ×2
c++11 ×1
cmake ×1
d ×1
dependencies ×1
friend ×1
idioms ×1
unique-ptr ×1
wrapper ×1