Я использую Wand-py (реализация оболочки Wand вокруг ImageMagick), чтобы импортировать некоторые довольно продвинутые SVG и до сих пор видели отличные результаты! Это весь код, который он принимает:
with wand.image.Image( blob=svg_file.read(), format="svg" ) as image:
png_image = image.make_blob("png")
Я только что открыл это сегодня и почувствовал, что это стоит того, чтобы поделиться тем, кто может протиснуться через этот ответ, поскольку прошло какое-то время, поскольку большинство из этих вопросов .
ПРИМЕЧАНИЕ: Технически при тестировании я обнаружил, что вам даже не нужно передавать параметр формата для ImageMagick, поэтому with wand.image.Image( blob=svg_file.read() ) as image:
было всем, что было действительно необходимо .
РЕДАКТИРОВАТЬ: Из попытки отредактировать qris вот какой полезный код, который позволяет использовать ImageMagick с SVG с прозрачным фоном:
from wand.api import library
import wand.color
import wand.image
with wand.image.Image() as image:
with wand.color.Color('transparent') as background_color:
library.MagickSetBackgroundColor(image.wand,
background_color.resource)
image.read(blob=svg_file.read(), format="svg")
png_image = image.make_blob("png32")
with open(output_filename, "wb") as out:
out.write(png_image)
Первый объявляет шаблон struct
с именем Example
, принимая любое количество типов:
template<typename... S>
struct Example /* <S...> */ ;
Если за именем вновь объявленного шаблона следует <>
, с или без аргументов это будет специализация!
Вторая определяет частичную специализацию хотя бы для одного аргумента типа:
template<typename H, typename... T>
struct Example<H, T...>
{
static const size_t value = sizeof(H) + Example<T...>::value;
};
И последняя определяет полную специализацию для типа нет -arguments:
template<>
struct Example<>
{
static const size_t value = 0;
};
Обратите внимание, что за template
следуют пустые <>
-кобки.
Неважно, что частичная специализация определена до полной специализации, потому что создание экземпляров должно быть отложено до тех пор, пока не будут известны аргументы типа шаблонов.
Конкретный экземпляр, который вы используете, Example<long,int,char>::value
, зависит от Example<int, char>::value
, который зависит от Example<char>
, что приводит к базовому случаю:
Example<long, int, char>::value = sizeof(long) + Example<int, char>::value; // sizeof(long) + sizeof(int) + 1 + 0
Example<int, char>::value = sizeof(int) + Example<char>::value; // sizeof(int) + 1 + 0
Example<char>::value = sizeof(char) + Example<>::value; // 1 + 0
Example<>::value = 0;
Конечно, пример может быть Упрощенно:
template <class... T>
struct Example {
static const size_t value = 0;
static_assert(!sizeof...(T), "The base-template only handles no template arguments.");
};
template <class H, class... T>
struct Example {
static const size_t value = sizeof(H) + Example<T...>::example;
};
Или с C ++ 17 выражениями фолда:
template <class... T>
struct Example {
static const size_t value = 0 + ... + sizeof(T);
};
В дополнение, есть веские причины никогда не использовать using namespace std;
, интересно, почему вы [ 1115], и return 0;
является избыточным для main()
.
Только отвечая на эту часть вашего вопроса:
Кроме того, почему в первом объявлении template + class отсутствует
blockquote>< S...>
сразу после объявления struct? (См., Что закомментировано)? когда это правильно добавить, а когда нет?
Когда вы делаете (общее) объявление шаблонной функции / class / struct / тип, вы используете угловую скобку
< >
только один раз, перед объявлением:template <typename T> void foo(T x);
Когда вы объявляете конкретную реализацию общего шаблона, вы используйте
< >
дважды, один раз перед объявлением, затем снова с определенными параметрами шаблона, для которых вы запускаете:template <> void foo<int>(int& x);
Когда вы объявляете конкретную специализацию общего шаблона, вы используете
< >
один раз с конкретными параметрами шаблона, для которых вы создаете экземпляр:template void foo<int>(int& x);
Подробнее о последних двух пунктах (и чем они отличаются):
Разница между созданием экземпляров и специализацией в шаблонах c ++
Кроме того, почему в первом объявлении шаблона + класса отсутствует "< S ...>" сразу после объявления структуры? (посмотрите, что закомментировано)? когда это правильно добавлять, а когда нет?
blockquote>Мне кажется, что лучше начать с этого момента.
Прежде всего, следующее (удалено
<S...>
прокомментировано) является объявлением (внимание: только объявление, не определение) структуры шаблонаExample
, которая получает список переменных типа шаблона параметрыtemplate<typename... S> struct Example;
Вы также можете избежать использования
S
и писать простоtemplate <typename...> struct Example;
, потому что имя списка переменных не используется в этом контексте.
На данный момент компилятор знает, что существует шаблонная структура с переменным числом аргументов
Example
, но не знает, как это делается.Затем мы добавляем определение специализации из
Example
, которые получают один или несколько параметров шаблона (обратите внимание, чтоExample
определено для получения ноля или более параметров, поэтому специализация, которая получает один или несколько параметров, является частным случаемExample
)//....... one --> V VVVVV <- or more template parameter template<typename H, typename... T> struct Example<H, T...> { // .........^^^^^^^^^ <- this is a specialization static const size_t value = sizeof(H) + Example<T...>::value; };
Часть
]<H, T...>
послеExample
идентифицирует специализацию (как сказано).Эта специализация определяет переменную
static const size_t
, инициализированную суммойsizeof(H)
(sizeof()
параметра шаблона первого типа) сvalue
, определенным в другом классеExample
:Example<T...>
. [ 1170]Итак, вы наблюдаете рекурсивное определение: значение - это сумма
sizeof()
первого параметра (типа) с суммойsizeof()
следующих типов.Предложение: если вы используете вариационные шаблоны, вы также можете использовать
constexpr
, поэтому лучше определитеvalue
какconstexpr
static constexpr std::size_t value = sizeof(H) + Example<T...>::value;
Или, что лучше, вы можете наследовать от
std::integral_constant
[1173 ]template <typename H, typename... T> struct Example <H, T...> : public std::integral_constant<std::size_t, sizeof(H) + Example<T...>{}> { };
, поэтому вы наследуете
value
изstd::integral_constant
с дополнительными полезными возможностями (например: автоматическое преобразование вstd::size_t
в контексте, где требуетсяstd::size_t
)Каждая рекурсия нуждается в наземном обосновании, поэтому у вас есть
template<> struct Example<> { static const size_t value = 0; };
декларация другой специализации из
Example
; на этот раз случай с точно нулевым параметром шаблона (Example<>
). В этом случае у вас есть определениеvalue
, равное нулю, чтобы завершить рекурсию.Как и раньше, вы можете определить
value
какconstexpr
или, лучше IMHO, снова используяstd::integral_constant
template <> struct Example<> : public std::integral_constant<std::size_t, 0u> { };
Теперь вы определили две специализации для
Example
: одна для одной - или больше вариантов параметров, один для случая нулевых параметров. Итак, вы рассмотрели все случаи дляExample
, который объявлен получающим ноль или более параметров; нет необходимости объявлять универсальную (не специализированную версию)Example
.Как заметил Deduplicator, вы можете определить общий случай и только одну специализацию: если вы напишите
template <typename...> struct Example : public std::integral_constant<std::size_t, 0u> { }; template <typename T, typename ... Ts> struct Example<T, Ts...> : public std::integral_constant<std::size_t, sizeof(T)+Example<Ts...>{}> { };
, вы сначала объявите
Example
, получая ноль или более параметров и определите общий случай сvalue
нулем (основной случай), затем вы определяете одну или более специализацию.Учитывая, что компилятор выбирает более специализированную версию (когда подходит больше версий), компилятор выбирает специализацию при наличии одного или нескольких параметров (версии ботов совпадают, но специализация более специализирована) и универсальную версию при наличии являются нулевыми параметрами (потому что специализация не совпадает).
1182] Этот способ немного более синтетический, но может быть менее понятным.
Не могли бы вы описать, что произойдет для вызова ниже? какой из шаблонов будет использоваться и когда?
blockquote>Теперь должно быть легко понять.
Когда вы пишете
Example<long, int, char>::value
, вы запрашиваете
value
изExample<long, int, char>
.Три параметра, поэтому выбирается одна или несколько специализаций, то есть
value = sizeof(long) + Example<int, char>::value;
по той же причине,
value
вExample<int, char>
равноvalue = sizeof(int) + Example<char>::value;
] и
value
вExample<char>
равноvalue = sizeof(char) + Example<>::value;
Теперь для
Example<>::value
выбрана специализация нулевых параметров иExample<>::value
равно нулю.Итак, мы имеем, что
value
вExample<long, int, char>
инициализируется сvalue = sizeof(long) + sizeof(int) + sizeof(char) + 0;
Вы пометили C ++ 11, поэтому жаль, что вы не можете использовать C ++ 17 (сворачивание шаблонов ), где вы можете вообще избежать рекурсии и определить
Example
какusing
template <typename ... Ts> using Example = std::integral_constant<std::size_t, (... + sizeof(Ts))>;