0

I'm trying to write something similar to this:

void loadImage(SDL_Renderer *ren, {SDL_Texture *&texture, const char *imagePath} ... ){
    for(int i = 0; i < numOfPairs; i++){
        SDL_Surface *curImage = IMG_Load(pairs[i].second);
        pairs[i].first = SDL_CreateTextureFromSurface(ren, curImage);
        SDL_FreeSurface(curImage);
    }
}

Where I have a variable number of pairs and each pair contains a texture and its correspondent path. I've no idea, which way would be best to approach this problem. I've thought about using the <cstdarg> library's variadic functions but I read on another question that that's not a good solution.

SandWood Jones
  • 104
  • 1
  • 9

3 Answers3

1

the way that immediately comes to mind is std::vector. You could pass a vector of std::pair or of std::tuple if you are using a c++11 or better compiler.

very simple to do

#include <vector>
#include <tuple>

....


std::vector<std::tuple<SDL_texture, const char *>> args;

args.push_back(std::make_tuple(texture1, path1));
args.push_back(std::make_tuple(texture2, path2));

then your func

    void load_Image(SDL_renedred *ren, const std::vector<std::tuple<SDL_texture, const char *>> &args)
    {
         std::tuple<SDL_textture, const char*> &arg1 = args[0];
  // or if you have modern c++
         auto &arg1 = args[0];  
    }

(not compiled, probably got typos in it.)

pm100
  • 48,078
  • 23
  • 82
  • 145
0

First of all write a function that will process each pair (i.e. what you have inside your for loop):

using ImagePair = std::pair<SDL_Texture*&, const char*>;

void processImage(ImagePair imgPair)
{
    SDL_Surface *curImage = IMG_Load(imgPair.second);
    imgPair.first = SDL_CreateTextureFromSurface(ren, curImage);
    SDL_FreeSurface(curImage);
}

Then if you have C++11 or above, you can use a brace-initialization trick to call processImage() for each argument:

template <typename ... Ts>
void loadImage(Ts ... ts)
{
    using dummy = int[];
    (void)dummy {0, (processImage(ts)), 0)... };
}

What you're doing here is taking advantage of the fact that the compiler knows it has to do a pack expansion inside a brace-initialization list. That way you can avoid having to write recursive variadic template functions yourself.

Here you are building a dummy array of integers, which is equal in size to the number of variadic arguments you pass in. For each argument you call the function processImage(), but using the comma operator you set the value in the array to 0. So you expand the pack and call processImage() for each argument while creating a dummy array. This answer might explain it more clearly.

If you have C++17 or above, you can simplify further and use a fold expression:

template<typename... Ts>
void loadImage(Ts... args)
{
    (processImage(args),...);
}
jignatius
  • 6,304
  • 2
  • 15
  • 30
0

Although I hate it, you can use std::initializer_list like:

#include <initializer_list>
#include <tuple>

void loadImage(SDL_Renderer *ren, std::initializer_list<std::tuple<SDL_Texture*&,const char *&>> pair_list ){
    for(auto p:pairlist){//initializer_list cannot be indexed but only iterated
        SDL_Surface *curImage = IMG_Load(p.second);
        p.first = SDL_CreateTextureFromSurface(ren, curImage);
        SDL_FreeSurface(curImage);
    };  
};

//call site:
loadImage(ren,{tie(tex1,path1),tie(tex2,path2),tie(tex3,path3)});

It is recommended to store your pointers in std::unique_ptr instants with costume deleter. And I guess you'd better change your data structue design to avoid tying objects like that - e.g. using a std::map:

#include <initializer_list>
#include <memory>
#include <map>

struct MySDLDeleter{
    void operator(SDL_Texture* texPtr){SDL_FreeTexture(texPtr);};
    void operator(SDL_Renderer * renPtr){SDL_FreeRenderer(renPtr);};
    void operator(SDL_Surface * srfPtr){SDL_FreeSurface(srfPtr);};
};

auto loadImage(std::unique_ptr<SDL_Renderer,MySDLDeleter> ren, std::initializer_list<const char *> path_list ){
    std::map<const char *,std::unique_ptr<SDL_Texture,MySDLDeleter> texMap;
    for(auto p:path_list ){
        std::unique_ptr<SDL_Surface ,MySDLDeleter> curImage = IMG_Load(p.second);
        texMap.insert(p,SDL_CreateTextureFromSurface(ren.get(), curImage.get()));
    }; 
    return texMap; 
};

//call site:
auto texMap=loadImage(create_the_renderer_ptr(),{path1,path2,path3});
Red.Wave
  • 2,790
  • 11
  • 17