In this article we will see how to use std::generate and std::generate_n with STL sequential containers.

std::generate is a STL algorithm useful in generating data for a sequential container by calling passed function object continuously. For example,

Suppose, we have a structure Task and a std::vector<Task * > and we want to initialize this vector with 10 different Tasks. To create a Task object we already has a TaskFactory Class and we want to use this factory.

Task Structure contains an id and name i.e.

TaskFactory is a function object and has two different methods to generate Task Objects. Each object will have a unique ID and name i.e

Now to initialize a vector<Task *> with 10 different Task Objects simple approach can be using for loop to traverse the vector and insert in it.

But, can we improve this?

yes we can. Using std::generate STL algorithm.

std::generate

std::generate generates the values for given range with the help of given function object i.e.

std::generate(<RANGE START>, <RANGE_END>, <FUNCTION_OBJECT/POINTER>);

It internally traverse the range and for each entry in this range it calls the passed function object/pointer and assigns the result back to that position.

Lets rewrite the above code to insert Task objects in std::vector<Task * > through TaskFactory.

Important Point : std::generate and std::generate_n expects that vector has proper size i.e. you need to set vector size before using it like we have set the vector size to default 10 while creating i.e. std::vector<Task *> vecTasks(10);

As we can see this Task Factory is a Function Object therefore we are passing it in std::generate directly.

But what if this TaskFactory is not a Function object i.e. what if there is no operator()() defined in TaskFactory.

In that case we need to use this getTask() function directly with std::generate. How to do that?

We can do that by converting Taskfactory::getTask() into a function object with help of std::bind and std::mem_fun i.e.

To learn more about std::bind and std::mem_fun checkout our seperate article on them.

There is also another version of std::generate i.e. std::generate_n()

std::generate_n

It generates the values first n entries of the passed sequence by calling passed function object/pointer.

std::generate_n(<OUTPUT_ITERATOR>, <NUMBER_OF_ELEMENTS>,<FUNCTION_OBJECT/POINTER>);

For example for above code lets generate Task Object for first 5 entries in vector<Task *> instead of complete 10.

Complete working code is as follows,


#include <iostream>
#include <algorithm>
#include <string>
#include <vector>
#include <functional>

struct Task
{
    int m_id;
    std::string m_name;
    Task(int id, std::string name) :
            m_id(id), m_name(name)
    {}
};

class TaskFactory
{
    static int m_counter;
public:
    Task * getTask()
    {
        m_counter++;
        Task * taskPtr = new Task(m_counter, "Task_"+ std::to_string(m_counter));
        return taskPtr;
    }
    Task * operator ()()
    {
        return getTask();
    }

};
int TaskFactory::m_counter = 0;

void approach_1(std::vector<Task *> & vecList)
{
    TaskFactory factory;
    for(int i = 0 ; i < 10; i++)
    {
        Task * ptr = factory.getTask();
        vecList[i] = ptr;
    }
}

void approach_3(std::vector<Task *> & vecList)
{
    TaskFactory factory;
    std::generate(vecList.begin(), vecList.end(), std::bind(std::mem_fun(&TaskFactory::getTask), &factory));
}
void approach_2(std::vector<Task *> & vecList)
{
    TaskFactory factory;
    std::generate(vecList.begin(), vecList.end(), TaskFactory());
}

void displayTasks(std::vector<Task *> & vecList)
{
    std::for_each(vecList.begin(), vecList.end(), [](const Task * taskPtr){
        std::cout<<taskPtr->m_id<<" :: "<<taskPtr->m_name<<std::endl;
    });
}

void generate_n_example(std::vector<Task *> & vecList)
{
    std::generate_n (vecList.begin(), 5, TaskFactory());
}
int main()
{

    std::vector<Task *> vecList(10);

    approach_1(vecList);
    displayTasks(vecList);

    approach_2(vecList);
    displayTasks(vecList);

    approach_3(vecList);
    displayTasks(vecList);

    std::vector<Task *> vecList_2(10);
    generate_n_example(vecList_2);
    std::for_each(vecList_2.begin(), vecList_2.begin() + 5, [](const Task * taskPtr){
            std::cout<<taskPtr->m_id<<" :: "<<taskPtr->m_name<<std::endl;
        });

    return 0;

}

Click Here to Subscribe for more Articles / Tutorials like this.