简体   繁体   English

如何将常量数组作为参数传递给 C++ 函数/方法

[英]How to pass a constant array as argument to a C++ function/method

Problem:问题:
I have a C++11 method (CImg class) that takes in input an unsigned char[3] array to specify the color.我有一个 C++11 方法(CImg 类),它接受输入一个unsigned char[3]数组来指定颜色。 I'm compiling with a mingw compiler and -std=gnu++11 option.我正在使用 mingw 编译器和-std=gnu++11选项进行编译。
For reference this is the method I need to call:作为参考,这是我需要调用的方法:

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);

If I try to construct the array locally I get the following error.如果我尝试在本地构建数组,我会收到以下错误。 The array is created and destroyed before being passed, making the argument invalid when the function is called.数组在被传递之前被创建和销毁,使得在调用 function 时参数无效。

image.draw_line( 20, 90, 190, 10, (unsigned char[3]){0,255,0}, 0.9 );
"ERROR: taking address of temporary array"

Attempt 2: Local array declared before function call using local var尝试 2:在使用本地 var 调用 function 之前声明的本地数组
Researching the issue, the most common solution is to create an explicit local var.研究这个问题,最常见的解决方案是创建一个显式的本地变量。 It clearly works since it's destroyed later, when it goes out of scope.它显然可以工作,因为它后来被销毁,当它从 scope 中消失时。 It obviously works, but I really feel there should be a more readable and less verbose way of doing it.它显然有效,但我真的觉得应该有一种更易读、更简洁的方法。

unsigned char my_temp_color[3] = {0,255,0};
image.draw_line( 20, 90, 190, 10, my_temp_color, 0.9 );

Attempt 3: Constant local array尝试 3:常量局部数组
I fooled around with different ways to pass the array.我用不同的方式来传递数组。 I tried with structures, etc... My mingw compiler is happy with passing a local constant array and does not give any warnings or errors.我尝试了结构等...我的 mingw 编译器很高兴传递一个本地常量数组,并且没有给出任何警告或错误。

image.draw_line( 20, 90, 190, 10, (const unsigned char[3]){0,255,0}, 0.9 );

Proposed Solution 1: std::array建议的解决方案 1:std::array
If I try passing a naked std::array, the compiler cannot resolve the type to unsigned char[3]如果我尝试传递一个裸 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)"

It works if I pass a local std::array using.data() method to get the pointer, but it is even more verbose than a regular array.如果我通过本地 std::array using.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 );

It works if I do it all inline.如果我全部内联,它会起作用。 With this solution I'm not sure on where the object is created/destroyed and I'm worried about the pointer becoming invalid and passing trash data silently.使用此解决方案,我不确定 object 的创建/销毁位置,我担心指针变得无效并静默传递垃圾数据。

image.draw_line( 20, 90, 190, 10, std::array<unsigned char,3>{ {0,255,0} }.data(), 0.9 );

Proposed Solution 2: std::string建议的解决方案 2:std::string
The initialization is harder to read with std::string.使用 std::string 更难读取初始化。 If I try passing a naked std::string, the compiler cannot resolve the type to unsigned char[3]如果我尝试传递一个裸 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)'"

Like std::array it works if I i use.data() method, but I have the same worries.就像 std::array 一样,如果我使用 .data() 方法,它就可以工作,但我也有同样的担忧。

Proposed Solution 3: wrapper + structures提议的解决方案3:包装器+结构
The third solution proposed by @jarod42 is to create wrappers around the third party library and use a more convenient and readable interface. @jarod42 提出的第三种解决方案是围绕第三方库创建包装器,并使用更方便和可读的界面。 Being able to change the interface allows to use structures that solve all problems of scoping and temporary values.能够更改接口允许使用解决范围和临时值的所有问题的结构。 It also has the added benefit of adding an abstraction layer and making future changes easier.它还具有添加抽象层并使未来更改更容易的额外好处。 I decided to incapsulate wrappers and structures as static methods inside a class.我决定将包装器和结构封装为 class 中的 static 方法。 I really like this solution.我真的很喜欢这个解决方案。

The using statement can be used to make the code less verbose. using语句可用于使代码不那么冗长。 My compiler can also automatically deduce the type from just the brackets.我的编译器还可以自动从括号中推断出类型。 For the scope of this question I left it extended to understand what's going on.对于这个问题的 scope,我将其扩展以了解发生了什么。

//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;
}

output: output:
输出PNG图像

source code:源代码:
https://github.com/OrsoEric/2021-02-02-CImg-PNG-Test https://github.com/OrsoEric/2021-02-02-CImg-PNG-Test

Questions:问题:

  1. Is passing (const unsigned char[3]){0,255,0} C++11 compliant or it's just a quirk of the mingw compiler that makes it work?传递(const unsigned char[3]){0,255,0} C++11 是否符合要求,或者它只是 mingw 编译器的一个怪癖使它工作?
  2. Is there another way I have not considered to declare and pass the array as argument?还有另一种我没有考虑过声明并将数组作为参数传递的方法吗?
  3. With the following calls are the temporary objects destroyed after the statement (safe), or before the call (causing the pointer to potentially become invalid)?通过以下调用,临时对象是在语句之后(安全)还是在调用之前(导致指针可能变得无效)销毁?
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 );

When working with 3rd library, it is generally good to write wrapper around them.使用第三个库时,通常最好围绕它们编写包装器。

  • So you might have the interface you want,所以你可能有你想要的界面,
  • and you might easily change/upgrade the 3rd library if needed (you only have to adapt the wrappers):如果需要,您可以轻松更改/升级第三个库(您只需调整包装器):
struct Point
{
   int x = 0;
   int y = 0;
}

struct Color
{
    Color(unsigned char r, unsigned char g, unsigned char b, unsigned char a = 0) :
        rgba{r, g, b, a}
    {}

    // ...
    unsigned char rgba[4]{};
};

template<typename T>
void draw_line(CImg<T>& img, Point p1, Point p2, Color color)
{
    img.draw_line(p1.x, p1.y, p2.x, p2.y, color.rgba);
}

And so you might use:所以你可以使用:

draw_line(img, Point{20, 90}, Point{190, 10}, Color{0,255,0});
draw_line(img, {20, 90}, {190, 10}, {0,255,0});

Note: You might even hide CImg<T> in your own class/interface.注意:您甚至可以在自己的类/接口中隐藏CImg<T>

My simple answer is:我的简单回答是:

Forget about the old C-style arrays.忘记旧的 C 风格 arrays。 Do not use them at all.根本不要使用它们。

Use std::array if you know the size in advance and std::vector if you don't.如果您事先知道大小,请使用std::array ,如果不知道,请使用 std std::vector

Especially: Use std::string instead of char[] or char* .特别是:使用std::string而不是char[]char*

And pass the variables around using references.并使用引用传递变量。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM