0

In an effort to optimize my code below, it seems to me that it would be beneficial if I could pass a pointer to one of the member functions str1 and str2 as an argument to fill_vec instead of having two explicit loops in fill_vec.

Is there a preferred way to do that in C++11? Or do you suggest a different strategy?


#include <iostream>
#include <vector>
#include <map>

class Base
{
  private:
    std::map<int, std::string> m_base1, m_base2;
    std::vector<std::string> m_str1 = {"one", "two", "three"};
    std::vector<std::string> m_str2 = {"four", "five", "six"};

  public:
    std::vector<std::string> &str1() { return m_str1; }
    std::vector<std::string> &str2() { return m_str2; }

    std::map<int, std::string> &base1() { return m_base1; }
    std::map<int, std::string> &base2() { return m_base2; }
};

template <typename T>
void fill_vec(T *b)
{
    size_t counter = 0;
    for (const auto &str_iter : b->str1())
        (b->base1())[counter++] = str_iter;

    counter=0;
    for (const auto &str_iter : b->str2())
        (b->base2())[counter++] = str_iter;
}

int main(int argc, char *argv[])
{
    Base *b = new Base;
    fill_vec(b);

    return 0;
}
N08
  • 1,265
  • 13
  • 23

2 Answers2

0

Recommended practices would dictate that fill_vec() should be a member of Base, preferably called from the constructor, so the object would be ready to use right after creation.

But, since the maps m_base1 and m_base2 are constants, you should do away with m_str1 and m_str2, make m_base1 and m_base2 static and initialize them directly in your constructor.

And, you should use smart pointers whenever possible.

This gives:

#include <iostream>
#include <vector>
#include <map>
#include <string>
#include <memory>

class Base
{
  private:
    static std::map<int, std::string> m_base1, m_base2;

  public:
    Base();  // WARNING !! must create a object before using maps!

    static const auto & base1() { return m_base1; }
    static const auto & base2() { return m_base2; }
};


// in base.cpp

std::map<int, std::string> Base::m_base1;

Base::Base()
{
    if (m_base1.empty())
    {
        m_base1[0] = "one";
        m_base1[1] = "two";
        m_base1[2] = "three";
    }
    if (m_base2.empty())   
    {
        m_base2[0] = "four";  // doesn't look right, but equivalent to original code
        m_base2[1] = "five";
        m_base2[2] = "six";
    }
}


// in your main module..

int main(int argc, char *argv[])
{
    // auto b = std::unique_ptr<Base>(new Base{});
    // this would be best since Base is very small.
    Base b;

    // this prints the string "two six"
    std::cout << b.base1().at(1) << " " << b.base2().at(2) << '\n';

    return 0;
}
Michaël Roy
  • 6,338
  • 1
  • 15
  • 19
  • 1
    His `map` is 0-based, no? `m_base1[0] = "one";` – pingul Aug 24 '17 at 10:37
  • No, map is _not_ 0 based, std::map maps arbitrary integer values to a string. – Michaël Roy Aug 24 '17 at 10:40
  • Yes of course, but doing `m_base1[counter++]` with `counter = 0` will evaluate to 0. Also, note that you changed the signature of `m_base1, m_base2`: He did not have them static. – pingul Aug 24 '17 at 10:41
  • fixed. I guess this points to other errors in the OP's code. The maps are static because they were initialized with constants. – Michaël Roy Aug 24 '17 at 10:45
0

It is possible but I would, based on the above comment check the practicality of it.

Something like this would work:

template <typename T>
void fill_vec(Base* obj, std::map<int, std::string>& (T::*mapv)(), std::vector<std::string>& (T::*vec)())
{
    size_t counter = 0;
    for (const auto &str_iter : (obj->*vec)())
        (obj->*mapv)()[counter++] = str_iter;       
}

int main(int argc, char *argv[])
{
    Base *b = new Base;
    fill_vec(b, &Base::base1, &Base::str1);

    return 0;
}

This is working in a similar way to how you would normally pass pointer to member fields

Samer Tufail
  • 1,835
  • 15
  • 25