L1Vector.h 6.23 KiB
/* Copyright (C) 2021 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt
SPDX-License-Identifier: GPL-3.0-only
Authors: Sergey Gorbunov [committer] */
#ifndef L1Vector_H
#define L1Vector_H
/// @file L1Vector.h
/// @author Sergey Gorbunov
/// @date 2021-06-16
#include "L1Def.h"
#ifndef FAST_CODE
#include <FairLogger.h>
#endif
#include <sstream>
/// L1Vector class is a wrapper around std::vector.
/// It does the following:
/// 1. gives names to vectors for better debugging
/// 2. controls the out-of-range access in debug mode
/// 3. supresses methods that are currently not controlled
/// 4. warns when slow memory operations are called,
/// i.e. when the preallocated capacity is reached and the entire vector should be copied to a new place
/// 5. blocks usage of boolean vectors, as they have a special
/// space-optimized but slow implementation in std. (Use L1Vector<char> instead).
///
template<class T>
class L1Vector : private std::vector<T> {
public:
typedef std::vector<T> Tbase;
template<typename... Tinput>
L1Vector(Tinput... value) : Tbase(value...)
{
}
template<typename... Tinput>
L1Vector(const char* name, Tinput... value) : Tbase(value...)
, fName(name)
{
}
L1Vector(const L1Vector& v) : Tbase() { *this = v; }
L1Vector& operator=(const L1Vector& v)
{
fName = v.fName;
Tbase::reserve(v.capacity()); // make sure that the capacity is transmitted
Tbase::assign(v.begin(), v.end());
return *this;
}
void SetName(const std::string& s) { fName = s; }
void SetName(const std::basic_ostream<char>& s)
{
// helps to set a composed name in a single line via:
// SetName(std::stringstream()<<"my name "<<..whatever..);
fName = dynamic_cast<const std::stringstream&>(s).str();
}
std::string GetName() const
{
std::string s = " L1Vector<";
s += fName + "> ";
return s;
}
template<typename... Tinput>
void reset(std::size_t count, Tinput... value)
{
// does the same as Tbase::assign(), but works with the default T constructor too
// (no second parameter)
Tbase::clear();
Tbase::resize(count, value...);
}
template<typename... Tinput>
void enlarge(std::size_t count, Tinput... value)
{
if (count < Tbase::size()) {
LOG(FATAL) << "L1Vector \"" << fName << "\"::enlarge(" << count
<< "): the new size is smaller than the current one " << Tbase::size() << ", something goes wrong."
<< std::endl;
assert(count >= Tbase::size());
}
if ((!Tbase::empty()) && (count > Tbase::capacity())) {
LOG(WARNING) << "L1Vector \"" << fName << "\"::enlarge(" << count << "): allocated capacity of "
<< Tbase::capacity() << " is reached, the vector of size " << Tbase::size()
<< " will be copied to the new place." << std::endl;
}
Tbase::resize(count, value...);
}
void reduce(std::size_t count)
{
if (count > Tbase::size()) {
LOG(FATAL) << "L1Vector \"" << fName << "\"::reduce(" << count
<< "): the new size is bigger than the current one " << Tbase::size() << ", something goes wrong."
<< std::endl;
assert(count < Tbase::size());
}
Tbase::resize(count);
}
void reserve(std::size_t count)
{
if (!Tbase::empty()) {
LOG(FATAL) << "L1Vector \"" << fName << "\"::reserve(" << count << "): the vector is not empty; "
<< " it will be copied to the new place." << std::endl;
assert(Tbase::empty());
}
Tbase::reserve(count);
}
template<typename Tinput>
void push_back(Tinput value)
{
#ifndef FAST_CODE
if (Tbase::size() >= Tbase::capacity()) {
LOG(WARNING) << "L1Vector \"" << fName << "\"::push_back(): allocated capacity of " << Tbase::capacity()
<< " is reached, re-allocate and copy." << std::endl;
}
#endif
Tbase::push_back(value);
}
template<typename Tinput>
void push_back_no_warning(Tinput value)
{
Tbase::push_back(value);
}
template<typename... Tinput>
void emplace_back(Tinput&&... value)
{
#ifndef FAST_CODE
if (Tbase::size() >= Tbase::capacity()) {
LOG(WARNING) << "L1Vector \"" << fName << "\"::emplace_back(): allocated capacity of " << Tbase::capacity()
<< " is reached, re-allocate and copy." << std::endl;
}
#endif
Tbase::emplace_back(value...);
}
T& operator[](std::size_t pos)
{
#ifndef FAST_CODE
if (pos >= Tbase::size()) {
LOG(FATAL) << "L1Vector \"" << fName << "\": trying to access element " << pos
<< " outside of the vector of the size of " << Tbase::size() << std::endl;
assert(pos < Tbase::size());
}
#endif
return Tbase::operator[](pos);
}
const T& operator[](std::size_t pos) const
{
#ifndef FAST_CODE
if (pos >= Tbase::size()) {
LOG(FATAL) << "L1Vector \"" << fName << "\": trying to access element " << pos
<< " outside of the vector of the size of " << Tbase::size() << std::endl;
assert(pos < Tbase::size());
}
#endif
return Tbase::operator[](pos);
}
T& back()
{
#ifndef FAST_CODE
if (Tbase::size() == 0) {
LOG(FATAL) << "L1Vector \"" << fName << "\": trying to access element of an empty vector" << std::endl;
assert(Tbase::size() > 0);
}
#endif
return Tbase::back();
}
const T& back() const
{
#ifndef FAST_CODE
if (Tbase::size() == 0) {
LOG(FATAL) << "L1Vector \"" << fName << "\": trying to access element of an empty vector" << std::endl;
assert(Tbase::size() > 0);
}
#endif
return Tbase::back();
}
using Tbase::begin;
using Tbase::capacity;
using Tbase::clear;
using Tbase::end;
using Tbase::insert; //TODO:: make it private
using Tbase::pop_back;
using Tbase::rbegin;
using Tbase::reserve;
using Tbase::size;
using typename Tbase::iterator;
private:
std::string fName {"no name"};
using Tbase::assign; // use reset() instead
using Tbase::at;
using Tbase::resize;
};
///
/// std::vector<bool> has a special implementation that is space-optimized
/// and therefore slow and not thread-safe.
/// That is why one should use L1Vector<char> instead.
///
template<>
class L1Vector<bool> { /// Make sure that L1Vector<bool> is not used
};
#endif