Прямо из документации Apple: Создание CGPathRef из объекта NSBezierPath
Вот соответствующий код.
@implementation NSBezierPath (BezierPathQuartzUtilities)
// This method works only in OS X v10.2 and later.
- (CGPathRef)quartzPath
{
int i, numElements;
// Need to begin a path here.
CGPathRef immutablePath = NULL;
// Then draw the path elements.
numElements = [self elementCount];
if (numElements > 0)
{
CGMutablePathRef path = CGPathCreateMutable();
NSPoint points[3];
BOOL didClosePath = YES;
for (i = 0; i < numElements; i++)
{
switch ([self elementAtIndex:i associatedPoints:points])
{
case NSMoveToBezierPathElement:
CGPathMoveToPoint(path, NULL, points[0].x, points[0].y);
break;
case NSLineToBezierPathElement:
CGPathAddLineToPoint(path, NULL, points[0].x, points[0].y);
didClosePath = NO;
break;
case NSCurveToBezierPathElement:
CGPathAddCurveToPoint(path, NULL, points[0].x, points[0].y,
points[1].x, points[1].y,
points[2].x, points[2].y);
didClosePath = NO;
break;
case NSClosePathBezierPathElement:
CGPathCloseSubpath(path);
didClosePath = YES;
break;
}
}
// Be sure the path is closed or Quartz may not do valid hit detection.
if (!didClosePath)
CGPathCloseSubpath(path);
immutablePath = CGPathCreateCopy(path);
CGPathRelease(path);
}
return immutablePath;
}
@end
]
rdar: // 15758302 : NSBezierPath для CGPath.
Предполагая, что create
и destroy
являются свободными функциями (что, по-видимому, имеет место из фрагмента кода OP) со следующими сигнатурами:
Bar* create();
void destroy(Bar*);
Вы можете написать свой класс Foo
как это
class Foo {
std::unique_ptr<Bar, void(*)(Bar*)> ptr_;
// ...
public:
Foo() : ptr_(create(), destroy) { /* ... */ }
// ...
};
Обратите внимание, что вам не нужно писать какой-либо лямбда или пользовательский делетер, потому что destroy
уже является deleter.
Вы знаете, использование пользовательского делетера - не лучший способ, так как вам придется упомянуть его по всему вашему коду. Вместо этого , поскольку вам разрешено добавлять специализации к классам уровня пространства имен в ::std
, пока задействуются пользовательские типы, и вы уважаете семантику, сделайте следующее:
Специализируйте std::default_delete
:
template <>
struct ::std::default_delete<Bar> {
default_delete() = default;
template <class U, class = std::enable_if_t<std::is_convertible<U*, Bar*>()>>
constexpr default_delete(default_delete<U>) noexcept {}
void operator()(Bar* p) const noexcept { destroy(p); }
};
И, возможно, также std::make_unique()
:
template <>
inline ::std::unique_ptr<Bar> ::std::make_unique<Bar>() {
auto p = create();
if (!p) throw std::runtime_error("Could not `create()` a new `Bar`.");
return { p };
}
std
открывает совершенно новую банку червей. Также обратите внимание, что специализация std::make_unique
не допускается post C ++ 20 (поэтому этого не следует делать раньше), потому что C ++ 20 запрещает специализацию вещей в std
, которые не являются шаблонами классов (std::make_unique
шаблон функции). Обратите внимание, что вы также, вероятно, закончите с UB, если указатель, переданный в std::unique_ptr<Bar>
, не был выделен из create()
, но из какой-либо другой функции распределения.
– Justin
10 July 2018 в 20:18
std::default_delete
соответствует требованию исходного шаблона. Я бы предположил, что std::default_delete<Foo>()(p)
будет правильным способом записи delete p;
, поэтому, если delete p;
будет действительным для записи (т. Е. Если Foo
будет завершен), это не будет тем же самым поведением. Кроме того, если delete p;
недействителен для записи (Foo
является неполным), это будет определять новое поведение для std::default_delete<Foo>
, а не сохранять поведение одинаковым.
– Justin
10 July 2018 в 21:08
Это можно сделать с помощью lambda в C ++ 11 (проверено в G ++ 4.8.2).
Учитывая это многоразовое typedef
:
template<typename T>
using deleted_unique_ptr = std::unique_ptr<T,std::function<void(T*)>>;
Вы можете написать:
deleted_unique_ptr<Foo> foo(new Foo(), [](Foo* f) { customdeleter(f); });
Например, с помощью FILE*
:
deleted_unique_ptr<FILE> file(
fopen("file.txt", "r"),
[](FILE* f) { fclose(f); });
При этом вы получаете преимущества безопасной от исключения очистки с использованием RAII, без шума try / catch.
std::function
в определении или так?
– j00hi
8 July 2015 в 10:38
std::function
. В отличие от этого решения может быть встроен лямбда или пользовательский класс, как в принятом ответе. Но этот подход имеет преимущество в том случае, если вы хотите изолировать всю реализацию в выделенном модуле.
– magras
12 October 2015 в 17:57
deleted_unique_ptr<Foo> foo(new Foo(), customdeleter);
, если customdeleter
следует за соглашением (он возвращает void и принимает необработанный указатель в качестве аргумента).
– Victor Polevoy
5 September 2017 в 08:47
Если вам не удастся изменить делетера во время выполнения, я настоятельно рекомендую использовать настраиваемый тип удаления. Например, если вы используете указатель функции для вашего делета, sizeof(unique_ptr<T, fptr>) == 2 * sizeof(T*)
. Другими словами, половина байтов объекта unique_ptr
теряется.
Однако запись пользовательского удаления для обертывания каждой функции является проблемой. К счастью, мы можем написать шаблон, шаблонный для функции:
Поскольку C ++ 17:
template <auto fn>
using deleter_from_fn = std::integral_constant<decltype(fn), fn>;
template <typename T, auto fn>
using my_unique_ptr = std::unique_ptr<T, deleter_from_fn<fn>>;
// usage:
my_unique_ptr<Bar, destroy> p{create()};
До C ++ 17:
template <typename D, D fn>
using deleter_from_fn = std::integral_constant<D, fn>;
template <typename T, typename D, D fn>
using my_unique_ptr = std::unique_ptr<T, deleter_from_fn<D, fn>>;
// usage:
my_unique_ptr<Bar, decltype(destroy), destroy> p{create()};
Вы можете просто использовать std::bind
с вашей функцией destroy.
std::unique_ptr<Bar, std::function<void(Bar*)>> bar(create(), std::bind(&destroy,
std::placeholders::_1));
Но вы также можете использовать лямбда.
std::unique_ptr<Bar, std::function<void(Bar*)>> ptr(create(), [](Bar* b){ destroy(b);});
Вам просто нужно создать класс deleter:
struct BarDeleter {
void operator()(Bar* b) { destroy(b); }
};
и предоставить его как аргумент шаблона в unique_ptr
. Вам все равно придется инициализировать unique_ptr в ваших конструкторах:
class Foo {
public:
Foo() : bar(create()), ... { ... }
private:
std::unique_ptr<Bar, BarDeleter> bar;
...
};
Насколько я знаю, все популярные библиотеки c ++ реализуют это правильно; поскольку BarDeleter
фактически не имеет какого-либо состояния, ему не нужно занимать какое-либо пространство в unique_ptr
.
struct BarDeleter
) до std::unique_ptr
(std::unique_ptr<Bar, BarDeleter>
), который позволяет конструктору std::unique_ptr
создать экземпляр Deleter самостоятельно. то есть допустим следующий код std::unique_ptr<Bar, BarDeleter> bar[10];
– DavidF
8 July 2016 в 11:47
typedef std::unique_ptr<Bar, BarDeleter> UniqueBarPtr
– DavidF
8 July 2016 в 11:51
std::unique_ptr<Bar, decltype(&destroy)> ptr_;
– Joe 26 March 2015 в 18:32