Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Syntax options for accepting capturing lambdas as constructor args and storing them in class

I am trying to write a vector wrapper (indexed_vec) which stores objects of type ValueType but other datastructures (vectors of other types) refer to these by index (because iterators are clearly not stable).

So when objects of ValueType are deleted in indexed_vec, some housekeeping has to be done to keep the indeces in the other other datastructures up to date.

Therefore indexed_vec also stores 2 lambda’s which are effectively "subscribers" (Observer Pattern) to index changes.

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

The code below works, as desired, but the syntax for constructing the instance of indexed_vec seems clumsy.

Are there better alternatives? I investigated deduction guides, but that doesn’t work apparently.

A factory function?

(NOTE: the wrappers exposing of the inner vector as a public members will not stay like that, it’s just to reduce the code here)


#include <cstddef>
#include <iostream>
#include <vector>

template <typename ValueType, typename DeleteIndexCBType, typename ChangeIndexCBType>
struct indexed_vec {
    indexed_vec(DeleteIndexCBType& dcb_, ChangeIndexCBType& ccb_) : dcb(dcb_), ccb(ccb_) {}

    DeleteIndexCBType dcb;
    ChangeIndexCBType ccb;

    std::vector<ValueType> v;

    std::size_t erase(std::size_t index_to_erase) {
        // TODO handle empty vector
        v[index_to_erase] = v.back();
        v.pop_back();
        dcb(index_to_erase);
        ccb(v.size(), index_to_erase); // NOTE v.size() is NOW one off the end, but that's accurate
        return 1;
    }
};

template <typename T>
void print(const std::vector<T>& v) {
  for (auto& e: v) std::cout << e << " ";
  std::cout << '\n';
}
  

int main() {
    std::string context = "captured context";

    auto delete_subscriber = [&context](std::size_t idx) {
        std::cout << "deleter: " << context << ": " << idx << "\n";
    };
    auto change_subscriber = [&context](std::size_t old_idx, std::size_t new_idx) {
        std::cout << "updater: " << context << ": " << old_idx << " => " << new_idx << "\n";
    };

    // this seems clumsy?
    indexed_vec<std::size_t, decltype(delete_subscriber), decltype(change_subscriber)> v1(
        delete_subscriber, change_subscriber);

    v1.v.reserve(10);
    for (std::size_t v = 10; v != 20; ++v) v1.v.push_back(v);

    print(v1.v);
    v1.erase(3);
    print(v1.v);
}

>Solution :

One easy easy way to simplify the syntax is to wrap the verbose code in a factory function like

template <typename ValueType, typename DeleteIndexCBType, typename ChangeIndexCBType>
auto make_index_vector(const DeleteIndexCBType& dcb_, const ChangeIndexCBType& ccb_)
{
    return indexed_vec<ValueType, DeleteIndexCBType, ChangeIndexCBType>(dcb_, ccb_);
}

Using that allows you to declare v1 like

auto v1 = make_index_vector<std::size_t>(delete_subscriber, change_subscriber);
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading