Проблема.
У меня есть метод C++11 (класс CImg), который принимает на вход массив unsigned char[3]
для указания цвета. Я компилирую с помощью компилятора mingw и опции -std=gnu++11
.
Для справки, это метод, который мне нужно вызвать:
template<typename tc>
CImg<T>& draw_line(int x0, int y0, int x1, int y1, const tc *const color, const float opacity=1, const unsigned int pattern=~0U, const bool init_hatch=true);
Если я попытаюсь создать массив локально, я получу следующую ошибку. Массив создается и уничтожается перед передачей, что делает аргумент недействительным при вызове функции.
image.draw_line( 20, 90, 190, 10, (unsigned char[3]){0,255,0}, 0.9 );
"ERROR: taking address of temporary array"
Попытка 2: локальный массив, объявленный перед вызовом функции с помощью локальной переменной
При исследовании проблемы наиболее распространенным решением является создание явной локальной переменной. Он явно работает, так как он уничтожается позже, когда выходит за рамки. Очевидно, что это работает, но я действительно считаю, что должен быть более читабельным и менее многословным способом сделать это.
unsigned char my_temp_color[3] = {0,255,0};
image.draw_line( 20, 90, 190, 10, my_temp_color, 0.9 );
Попытка 3: Константный локальный массив
Я повозился с разными способами передачи массива. Я пробовал со структурами и т. д. Мой компилятор mingw доволен передачей локального массива констант и не выдает никаких предупреждений или ошибок.
image.draw_line( 20, 90, 190, 10, (const unsigned char[3]){0,255,0}, 0.9 );
Предлагаемое решение 1: std::array
Если я попытаюсь передать голый std::array, компилятор не сможет преобразовать тип в unsigned char[3]
image.draw_line( 20, 90, 190, 10, std::array<unsigned char,3>{ {0,255,0} }, 0.9 );
"error: no matching function for call to 'cimg_library::CImg<float>::draw_line(int, int, int, int, std::array<unsigned char, 3>, double)"
Это работает, если я передаю локальный std::array с помощью метода .data() для получения указателя, но это даже более подробно, чем обычный массив.
std::array<unsigned char,3> my_temp_color = { {0,255,0} };
image.draw_line( 20, 90, 190, 10, my_temp_color.data(), 0.9 );
Это работает, если я делаю все это встроенным. С этим решением я не уверен, где объект создается/уничтожается, и я беспокоюсь о том, что указатель станет недействительным и молча передаст мусорные данные.
image.draw_line( 20, 90, 190, 10, std::array<unsigned char,3>{ {0,255,0} }.data(), 0.9 );
Предлагаемое решение 2: std::string
Инициализацию сложнее читать с помощью std::string. Если я попытаюсь передать голый std::string, компилятор не сможет преобразовать тип в unsigned char[3]
image.draw_line( 20, 90, 190, 10, std::string("/0/255/0"), 0.9 );
"no matching function for call to 'cimg_library::CImg<float>::draw_line(int, int, int, int, std::__cxx11::string, double)'"
Как и std::array, он работает, если я использую метод .data(), но у меня те же проблемы.
Предлагаемое решение 3: оболочка + структуры
Третье решение, предложенное @jarod42, заключается в создании оболочек вокруг сторонней библиотеки и использовании более удобного и читаемого интерфейса. Возможность изменения интерфейса позволяет использовать структуры, которые решают все проблемы области видимости и временных значений. Это также имеет дополнительное преимущество, заключающееся в добавлении уровня абстракции и упрощении будущих изменений. Я решил инкапсулировать обертки и структуры как статические методы внутри класса. Мне очень нравится это решение.
Оператор using
можно использовать, чтобы сделать код менее подробным. Мой компилятор также может автоматически вывести тип только из квадратных скобок. В рамках этого вопроса я оставил его расширенным, чтобы понять, что происходит.
//Build option:
//-std=gnu++11
//STD
#include <string>
//CImg
#define cimg_use_png
#define cimg_display 0
#include "CImg.h"
using namespace cimg_library;
//PNG library
#include "png.h"
//CImg wrapper. Add an abstraction layer to CImg to use less verbose Point and Color structures
class CImg_wrapper
{
public:
//2D Point
typedef struct _Point_2d
{
int g_s32_x, g_s32_y;
} Point_2d;
//3 channel color
typedef struct _Color_3u8
{
unsigned char g_u8p_rgb[3];
} Color_3u8;
//Draw text on an image
template<typename T>
static void draw_text(CImg<T>& image, Point_2d origin, std::string text, Color_3u8 foreground_color, float opacity, int font_size )
{
image.draw_text(origin.g_s32_x, origin.g_s32_y, text.c_str(), foreground_color.g_u8p_rgb, 0, opacity, font_size);
return;
}
//Draw a line on an image
template<typename T>
static void draw_line(CImg<T>& image, Point_2d p1, Point_2d p2, Color_3u8 color, float transparency)
{
image.draw_line(p1.g_s32_x, p1.g_s32_y, p2.g_s32_x, p2.g_s32_y, color.g_u8p_rgb, transparency);
return;
}
}; //CImg_wrapper
//DEMO
int main(int argc, char** argv)
{
//Create image
CImg<float> image
(
//width
200,
//height
100,
//Depth. 1 for a 2D image
1,
//Number of channels
3
);
//draw text on the image
CImg_wrapper::draw_text(image, (CImg_wrapper::Point_2d){20, 10}, std::string("Shaka"), (CImg_wrapper::Color_3u8){0,0,255}, 0.9f, 24 );
//draw a line on the image
CImg_wrapper::draw_line(image, (CImg_wrapper::Point_2d){20, 90}, (CImg_wrapper::Point_2d){190, 10}, (CImg_wrapper::Color_3u8){0,255,0}, 0.9f );
//The compiler is able to deduce the type from just the brackets if needed
//CImg_wrapper::draw_line(image, {20, 90}, {190, 10}, {0,255,0}, 0.9f );
//Save image on file
image.save("file.png");
return 0;
}
исходный код:
https://github.com/OrsoEric/2021-02-02-CImg-PNG-Test
Вопросы:
- Соответствует ли передача
(const unsigned char[3]){0,255,0}
С++ 11 или это просто особенность компилятора mingw, которая заставляет его работать? - Есть ли другой способ, которым я не рассматривал объявление и передачу массива в качестве аргумента?
- При следующих вызовах временные объекты уничтожаются после оператора (безопасно) или до вызова (что может привести к тому, что указатель может стать недействительным)?
image.draw_line( 20, 90, 190, 10, std::array<unsigned char,3>{ {0,255,0} }.data(), 0.9 );
image.draw_line( 20, 90, 190, 10, std::string("/0/255/0").data(), 0.9 );
std::array
? - person churill   schedule 01.02.2021std::array
error: no matching function for call to 'cimg_library::CImg<float>::draw_line(int, int, int, int, std::array<unsigned char, 3>, double)'
- person 05032 Mendicant Bias   schedule 01.02.2021template<typename T> draw_line(CImg<T>&, Point p1, Point p2, Color);
- person Jarod42   schedule 01.02.2021draw_line_wrapper(image, Point(20,90), Point( 190,10), Color(0,255,0) );
- person 05032 Mendicant Bias   schedule 01.02.2021{0,255,0}
, должны быть названы. Не используйте магические константы в коде. Как насчетGreen
? - person n. 1.8e9-where's-my-share m.   schedule 01.02.2021typedef enum _Config { MY_PARAM=42 } Config;
для хранения параметров класса. Для этого вопроса я вырезал нерелевантный код. - person 05032 Mendicant Bias   schedule 01.02.2021