Commit 62d776be authored by Philipp Götze's avatar Philipp Götze

Switched to PMDK (former NVML) +++ Added some switch-case alternatives +++...

Switched to PMDK (former NVML) +++ Added some switch-case alternatives +++ moved KeyType before TupleType for consistency +++ Some fixes
parent 20d943c3
......@@ -5,17 +5,18 @@ A persistent analytical table structure for non-volatile memory.
Please see the file [COPYING](COPYING) for license information.
### TODOs:
- [ ] Tests
- [x] Tests
- [ ] Get rid of expensive transactions
- [x] Improve benchmarks
- [ ] Describe usage
- [ ] The BlockIterator currently suffers from bugs (SEGSIGV with too big structures)
### Installation ###
We use the C++17 standard, thus, you need a very recent C++ compiler and CMake (3.2 or newer) build environment.
We use the C++14 standard, thus, you need a recent C++ compiler and CMake (3.2 or newer) build environment.
In addition some third party libraries such as [Catch](https://github.com/philsquared/Catch) for testing, [Google Benchmark](https://github.com/google/benchmark.git) for benchmarking, [Format](https://github.com/fmtlib/fmt.git), and [NVML](https://github.com/pmem/nvml.git) are used
These, however, are either included or downloaded during the build process.
In addition some third party libraries such as [Catch](https://github.com/philsquared/Catch) for testing, [Google Benchmark](https://github.com/google/benchmark.git) for benchmarking, [Format](https://github.com/fmtlib/fmt.git), and [PMDK](https://github.com/pmem/pmdk.git) are used.
These, however, are both downloaded and included during the build process.
After cloning and switching into the project directory, you can build the repository with with:
```
......@@ -29,4 +30,7 @@ make test
### Usage ###
The test cases and benchmarks use the directory _/mnt/pmem/test_ for storing data. Thus, make sure the pmem device is mounted at _/mnt/pmem_ and the subfolder _test_ has write permissions for everybody. Alternatively, you can change the directory in [common.h](src/bench/common.h).
The test cases and benchmarks use the directory _/mnt/pmem/test_ for storing data. Thus, make sure the pmem device is mounted at _/mnt/pmem_ and the subfolder _test_ has write permissions for everybody.
Alternatively, you can change the directory in the customization section in [CMakeLists.txt](src/CMakeLists.txt).
A detailed usage description will follow soon.
Till then the general usage can be found out by looking at test cases and benchmarks.
......@@ -44,7 +44,7 @@ if (BUILD_GOOGLE_BENCH)
# Google Benchmark framework
download_project(PROJ benchmark
GIT_REPOSITORY https://github.com/google/benchmark.git
GIT_TAG master
GIT_TAG releasing
UPDATE_DISCONNECTED 1
QUIET
)
......@@ -64,17 +64,17 @@ add_custom_command(
endif()
#--------------------------------------------------------------------------------
# Non-Volatile Memory Library (pmem.io)
download_project(PROJ nvml
GIT_REPOSITORY https://github.com/pmem/nvml.git
GIT_TAG 1.3.1-rc2
# Peristent Memory Development Kit (pmem.io)
download_project(PROJ pmdk
GIT_REPOSITORY https://github.com/pmem/pmdk.git
GIT_TAG master
UPDATE_DISCONNECTED 1
QUIET
)
add_custom_command(
OUTPUT ${THIRD_PARTY_DIR}/nvml
COMMAND ${CMAKE_COMMAND} -E chdir ${nvml_SOURCE_DIR} $(MAKE)
COMMAND ${CMAKE_COMMAND} -E chdir ${nvml_SOURCE_DIR} $(MAKE) install prefix=${THIRD_PARTY_DIR}/nvml
OUTPUT ${THIRD_PARTY_DIR}/pmdk
COMMAND ${CMAKE_COMMAND} -E chdir ${pmdk_SOURCE_DIR} $(MAKE)
COMMAND ${CMAKE_COMMAND} -E chdir ${pmdk_SOURCE_DIR} $(MAKE) install prefix=${THIRD_PARTY_DIR}/pmdk
)
#--------------------------------------------------------------------------------
......
......@@ -8,7 +8,7 @@ macro( build_test arg )
add_executable( ${arg} "${arg}.cpp" $<TARGET_OBJECTS:MainTest>)
target_link_libraries( ${arg}
ptable
${NVML_LIBRARIES}
${PMDK_LIBRARIES}
${BENCHMARK_LIB}
)
endmacro( build_test )
......@@ -17,7 +17,7 @@ macro( build_bench arg )
add_executable( ${arg} "${arg}.cpp")
target_link_libraries( ${arg}
ptable
${NVML_LIBRARIES}
${PMDK_LIBRARIES}
${BENCHMARK_LIB}
)
endmacro( build_bench )
......
......@@ -21,7 +21,7 @@ set(PMEM_MNT_PATH "/mnt/pmem/test")
option(ENABLE_LOG "enables log output for e.g. debugging" OFF)
option(BUILD_TEST_CASES "build tests for PTable functionality" ON )
option(BUILD_GOOGLE_BENCH "build google benchmark" OFF)
option(BUILD_BENCHMARKS "build benchmarks for Ptable" OFF)
option(BUILD_BENCHMARKS "build benchmarks for Ptable" ON )
################################################################################
# End of customization section #
......@@ -77,18 +77,18 @@ else()
endif()
########################
# Non-Volatile Memory Library
# Persistent Memory Development Kit
########################
#
set (NVML_LIBRARIES
"${THIRD_PARTY_DIR}/nvml/lib/libpmemblk.a"
"${THIRD_PARTY_DIR}/nvml/lib/libpmemlog.a"
"${THIRD_PARTY_DIR}/nvml/lib/libpmemobj.a"
"${THIRD_PARTY_DIR}/nvml/lib/libpmempool.a"
"${THIRD_PARTY_DIR}/nvml/lib/libpmem.a"
set (PMDK_LIBRARIES
"${THIRD_PARTY_DIR}/pmdk/lib/libpmemblk.a"
"${THIRD_PARTY_DIR}/pmdk/lib/libpmemlog.a"
"${THIRD_PARTY_DIR}/pmdk/lib/libpmemobj.a"
"${THIRD_PARTY_DIR}/pmdk/lib/libpmempool.a"
"${THIRD_PARTY_DIR}/pmdk/lib/libpmem.a"
${DYLIB_LIBRARY}
)
include_directories("${THIRD_PARTY_DIR}/nvml/include")
include_directories("${THIRD_PARTY_DIR}/pmdk/include")
include_directories("${THIRD_PARTY_DIR}/variant/include")
#-----------------------------------------------------------------------------------------
#
......@@ -107,7 +107,7 @@ set(core_sources
core/PTableInfo.cpp
${THIRD_PARTY_DIR}/fmt
${THIRD_PARTY_DIR}/catch
${THIRD_PARTY_DIR}/nvml
${THIRD_PARTY_DIR}/pmdk
${THIRD_PARTY_DIR}/variant
)
......
......@@ -35,11 +35,11 @@
namespace ptable {
using nvml::obj::delete_persistent;
using nvml::obj::make_persistent;
using nvml::obj::p;
using nvml::obj::persistent_ptr;
using nvml::obj::transaction;
using pmem::obj::delete_persistent;
using pmem::obj::make_persistent;
using pmem::obj::p;
using pmem::obj::persistent_ptr;
using pmem::obj::transaction;
/**
* A persistent memory implementation of a B+ tree.
......@@ -195,7 +195,7 @@ class PBPTree {
* @param val the value that is associated with the key
*/
void insert(const KeyType &key, const ValueType &val) {
auto pop = nvml::obj::pool_by_vptr(this);
auto pop = pmem::obj::pool_by_vptr(this);
transaction::exec_tx(pop, [&] {
SplitInfo splitInfo;
......@@ -321,6 +321,7 @@ class PBPTree {
// for each key-value pair within the range call func
for (auto i = 0u; i < leaf->numKeys; i++) {
auto &key = leaf->keys.get_ro()[i];
if (key < minKey) continue;
if (key > maxKey) return;
auto &val = leaf->values.get_ro()[i];
......@@ -1028,7 +1029,7 @@ class PBPTree {
* Create a new empty leaf node
*/
persistent_ptr<LeafNode> newLeafNode() {
auto pop = nvml::obj::pool_by_vptr(this);
auto pop = pmem::obj::pool_by_vptr(this);
persistent_ptr<LeafNode> newNode = nullptr;
transaction::exec_tx(pop, [&] {
newNode = make_persistent<LeafNode>();
......@@ -1037,7 +1038,7 @@ class PBPTree {
}
void deleteLeafNode(persistent_ptr<LeafNode> node) {
auto pop = nvml::obj::pool_by_vptr(this);
auto pop = pmem::obj::pool_by_vptr(this);
transaction::exec_tx(pop, [&] {
delete_persistent<LeafNode>(node);
});
......@@ -1047,7 +1048,7 @@ class PBPTree {
* Create a new empty branch node
*/
persistent_ptr<BranchNode> newBranchNode() {
auto pop = nvml::obj::pool_by_vptr(this);
auto pop = pmem::obj::pool_by_vptr(this);
persistent_ptr<BranchNode> newNode = nullptr;
transaction::exec_tx(pop, [&] {
newNode = make_persistent<BranchNode>();
......@@ -1056,7 +1057,7 @@ class PBPTree {
}
void deleteBranchNode(persistent_ptr<BranchNode> node) {
auto pop = nvml::obj::pool_by_vptr(this);
auto pop = pmem::obj::pool_by_vptr(this);
transaction::exec_tx(pop, [&] {
delete_persistent<BranchNode>(node);
});
......
This diff is collapsed.
......@@ -24,14 +24,15 @@
#include <unistd.h>
#include "PTable.hpp"
using nvml::obj::make_persistent;
using nvml::obj::p;
using nvml::obj::persistent_ptr;
using nvml::obj::pool;
using nvml::obj::transaction;
using pmem::obj::make_persistent;
using pmem::obj::p;
using pmem::obj::persistent_ptr;
using pmem::obj::pool;
using pmem::obj::transaction;
using MyTuple = std::tuple<int, int, std::string, double>;
using PTableType = ptable::PTable<MyTuple, int>;
using MyKey = int;
using PTableType = ptable::PTable<MyKey, MyTuple>;
using Vector = std::vector<int>;
using VectorVector = std::vector<Vector>;
......@@ -45,8 +46,8 @@ struct root {
};
const std::string path = ptable::gPmemPath + "benchdb.db";
const auto NUM_TUPLES = 100 * 1000;
const auto POOL_SIZE = 1024 * 1024 * 256;
const auto NUM_TUPLES = 1000 * 1000;
const auto POOL_SIZE = 1024 * 1024 * 1024 * 1ull; // 1GB
const auto ALIGNMENT = hibit(NUM_TUPLES) + 1;
VectorVector pv;
......@@ -100,12 +101,7 @@ void insert (pool<root> &pop, const std::string &path, size_t entries) {
pop = pool<root>::create(path, LAYOUT, POOL_SIZE);
transaction::exec_tx(pop, [&] {
const auto tInfo = ptable::VTableInfo("MyTable", {
{"a", Int_Type},
{"b", Int_Type},
{"c", String_Type},
{"d", Double_Type}
}, Int_Type);
const auto tInfo = ptable::VTableInfo<MyKey, MyTuple>("MyTable", {"a","b","c","d"});
const auto dims = Dimensions({
{0, 10, ALIGNMENT},
{3, 10, ALIGNMENT}
......@@ -118,9 +114,13 @@ void insert (pool<root> &pop, const std::string &path, size_t entries) {
for (auto i = 0u; i < entries; i++) {
auto tup = MyTuple(i + 1,
(i + 1) * 100,
fmt::format("String #{0}", i),
fmt::format("String #{0}", i % 1000),
(i + 1) * 1.0);
start = std::chrono::high_resolution_clock::now();
if (i % (entries/100) == 0) {
std::cout << "Inserting tuple: " << (i+1)*100/entries << "%\r";
std::cout.flush();
}
pTable->insert(i + 1, tup);
end = std::chrono::high_resolution_clock::now();
auto diff = std::chrono::duration_cast<std::chrono::microseconds>(end - start).count();
......
......@@ -43,7 +43,7 @@ static void BM_PointQuery(benchmark::State& state) {
for (auto _ : state) {
auto ptp = pTable->getByKey(state.range(1));
if (ptp.getNode() != nullptr) ptp.createTuple();
if (ptp.getNode() != nullptr) ptp;// ptp.createTuple();
else std::cerr << "key not found" << '\n';
}
pop.close();
......
......@@ -49,7 +49,7 @@ static void BM_RangeScan(benchmark::State &state) {
static void KeyRangeArguments(benchmark::internal::Benchmark* b) {
for (const auto &arg : KEY_RANGES) b->Args(arg);
}
BENCHMARK(BM_RangeScan)->Apply(KeyRangeArguments);
//BENCHMARK(BM_RangeScan)->Apply(KeyRangeArguments);
static void BM_NonKeyRangeScan(benchmark::State &state) {
......@@ -77,7 +77,30 @@ static void BM_NonKeyRangeScan(benchmark::State &state) {
static void NonKeyRangeArguments(benchmark::internal::Benchmark* b) {
for (const auto &arg : NON_KEY_RANGES) b->Args(arg);
}
BENCHMARK(BM_NonKeyRangeScan)->Apply(NonKeyRangeArguments);
//BENCHMARK(BM_NonKeyRangeScan)->Apply(NonKeyRangeArguments);
static void BM_PBPTreeKeyScan(benchmark::State &state) {
pool<root> pop;
if (access(path.c_str(), F_OK) != 0) {
insert(pop, path, NUM_TUPLES);
} else {
PLOG("Warning: " << path << " already exists");
pop = pool<root>::open(path, LAYOUT);
}
auto pTable = pop.get_root()->pTable;
for (auto _ : state) {
/* Scan via Index scan */
pTable->rangeScan2(state.range(0), state.range(1), [](int k, const PTuple<int, MyTuple> tp){tp;});
}
pop.close();
//std::remove(path.c_str());
}
BENCHMARK(BM_PBPTreeKeyScan)->Apply(KeyRangeArguments);
static void BM_PBPTreeScan(benchmark::State &state) {
......@@ -96,18 +119,20 @@ static void BM_PBPTreeScan(benchmark::State &state) {
/* Scan via PTuple iterator */
auto eIter = pTable->end();
auto iter = pTable->select(
[](const PTuple<MyTuple, int> &tp) {
return (tp.get<0>() >= KEY_RANGES[0][0]) && (tp.get<0>() <= KEY_RANGES[0][0]);
[&](const PTuple<int, MyTuple> &tp) {
return (tp.get<0>() >= state.range(0)) && (tp.get<0>() <= state.range(1)) &&
(tp.get<3>() >= state.range(2)) && (tp.get<3>() <= state.range(3));
});
for (; iter != eIter; iter++) {
(*iter).get<0>();
iter;
//(*iter).get<0>();
}
}
pop.close();
std::remove(path.c_str());
//std::remove(path.c_str());
}
BENCHMARK(BM_PBPTreeScan);
BENCHMARK(BM_PBPTreeScan)->Apply(NonKeyRangeArguments);
BENCHMARK_MAIN();
......@@ -31,8 +31,8 @@
namespace ptable {
using nvml::obj::allocator;
using nvml::obj::p;
using pmem::obj::allocator;
using pmem::obj::p;
/**************************************************************************//**
* \brief This type is used for defining the dimensions for the table.
......
......@@ -22,6 +22,7 @@
#include <array>
#include <unordered_map>
#include <vector>
#include "config.h"
#include <libpmemobj++/allocator.hpp>
......@@ -29,9 +30,9 @@
namespace ptable {
using nvml::obj::allocator;
using nvml::obj::p;
using nvml::obj::persistent_ptr;
using pmem::obj::allocator;
using pmem::obj::p;
using pmem::obj::persistent_ptr;
/** Positions in NVM_Block */
constexpr int gBDCCRangePos1 = 0;
......@@ -48,7 +49,7 @@ constexpr int gAttrOffsetSize = 4;
constexpr int gOffsetSize = 2;
/** The size of a single block in persistent memory */
static constexpr std::size_t gBlockSize = 1 << 12; // 12->4KB, 15->32KB, max 16 due to data types
static constexpr std::size_t gBlockSize = 1 << 15; // 12->4KB, 15->32KB, max 16 due to data types
/**
* \brief This type represents a byte array used for persistent structures.
......@@ -92,7 +93,7 @@ struct DataNode {
DataNode() : next(nullptr) {}
DataNode(BDCC_Block _block) : next(nullptr), block(_block) {}
persistent_ptr<struct DataNode> next;
persistent_ptr<DataNode> next;
p<BDCC_Block> block;
p<KeyVector> keys;
p<DeletedVector> deleted;
......
......@@ -6,13 +6,12 @@ Taken from: https://pmem.io/2017/01/23/cpp-strings.html
#define PString_hpp_
#include <libpmemobj/tx_base.h>
#include <libpmemobj++/make_persistent.hpp>
#include <libpmemobj++/make_persistent_array.hpp>
#include <libpmemobj++/persistent_ptr.hpp>
using nvml::obj::delete_persistent;
using nvml::obj::make_persistent;
using nvml::obj::persistent_ptr;
using pmem::obj::delete_persistent;
using pmem::obj::make_persistent;
using pmem::obj::persistent_ptr;
namespace ptable {
......
......@@ -21,7 +21,7 @@
using namespace ptable;
PColumnInfo::PColumnInfo(pool_base pop) : PColumnInfo(pop, "", Void_Type) {};
PColumnInfo::PColumnInfo(pool_base pop) : PColumnInfo(pop, "", ColumnType::VoidType) {};
PColumnInfo::PColumnInfo(pool_base pop, Column col) : PColumnInfo(pop, col.first, col.second) {};
......@@ -30,42 +30,4 @@ PColumnInfo::PColumnInfo(pool_base pop, const std::string &n, ColumnType ct) : t
name = make_persistent<char[]>(n.length() + 1);
strcpy(name.get(), n.c_str());
});
}
PTableInfo::PTableInfo(const VTableInfo &_tInfo, ColumnType _keyType) : keyType(_keyType) {
auto pop = pool_by_vptr(this);
transaction::exec_tx(pop, [&] {
name.set(const_cast<std::string *>(&_tInfo.name));
columns = make_persistent<PColumnVector>();
for (const auto &c : _tInfo)
columns->push_back(PColumnInfo(pop, c.first, c.second));
});
}
PTableInfo::PTableInfo(const std::string &_name, ColumnInitList _columns, ColumnType _keyType) :
keyType(_keyType) {
auto pop = pool_by_vptr(this);
transaction::exec_tx(pop, [&] {
name.set(const_cast<std::string *>(&_name));
columns = make_persistent<PColumnVector>();
for (const auto &c : _columns)
columns->push_back(PColumnInfo(pop, c.first, c.second));
});
}
void PTableInfo::setColumns(const ColumnVector vec) {
auto pop = pool_by_vptr(this);
transaction::exec_tx(pop, [&] {
delete_persistent<PColumnVector>(columns);
columns = make_persistent<PColumnVector>();
for (const auto &c : vec)
columns->push_back(PColumnInfo(pop, c.first, c.second));
});
}
int PTableInfo::findColumnByName(const std::string &colName) const {
for (std::size_t i = 0; i < (*columns).size(); i++) {
if (columns->at(i).getName() == colName) return (int) i;
}
return -1;
}
\ No newline at end of file
......@@ -20,8 +20,9 @@
#ifndef PTableInfo_hpp_
#define PTableInfo_hpp_
#include "VTableInfo.hpp"
#include "DataNode.hpp"
#include "PString.hpp"
#include "VTableInfo.hpp"
#include <libpmemobj++/allocator.hpp>
#include <libpmemobj++/detail/persistent_ptr_base.hpp>
......@@ -33,13 +34,13 @@
#include <libpmemobj++/transaction.hpp>
#include <libpmemobj++/utils.hpp>
using nvml::obj::delete_persistent;
using nvml::obj::make_persistent;
using nvml::obj::p;
using nvml::obj::persistent_ptr;
using nvml::obj::pool_base;
using nvml::obj::pool_by_vptr;
using nvml::obj::transaction;
using pmem::obj::delete_persistent;
using pmem::obj::make_persistent;
using pmem::obj::p;
using pmem::obj::persistent_ptr;
using pmem::obj::pool_base;
using pmem::obj::pool_by_vptr;
using pmem::obj::transaction;
namespace ptable {
......@@ -56,31 +57,65 @@ class PColumnInfo {
const ColumnType getType() const { return type.get_ro(); }
};
using PColumnVector = std::vector<PColumnInfo, nvml::obj::allocator<PColumnInfo>>;
using PColumnVector = std::vector<PColumnInfo, pmem::obj::allocator<PColumnInfo>>;
using PColumnIterator = PColumnVector::const_iterator;
template<typename KeyType, typename TupleType>
class PTableInfo {
PString name;
persistent_ptr<PColumnVector> columns;
p<ColumnType> keyType;
// p<ColumnType> keyType;
public:
PTableInfo() {}
PTableInfo(const VTableInfo &_tInfo, ColumnType _keyType = Void_Type);
PTableInfo(const std::string &_name, ColumnInitList _columns, ColumnType _keyType = Void_Type);
PTableInfo(const VTableInfo<KeyType,TupleType> &_tInfo) {
auto pop = pool_by_vptr(this);
transaction::exec_tx(pop, [&] {
name.set(const_cast<std::string *>(&_tInfo.name));
columns = make_persistent<PColumnVector>();
for (const auto &c : _tInfo)
columns->push_back(PColumnInfo(pop, c.first, c.second));
});
}
PTableInfo(const std::string &_name, std::initializer_list<std::string> _columns) {
auto pop = pool_by_vptr(this);
transaction::exec_tx(pop, [&] {
name.set(const_cast<std::string *>(&_name));
columns = make_persistent<PColumnVector>();
ColumnVector cv;
ColumnsConstructor<TupleType, std::tuple_size<TupleType>::value>::apply(cv, std::vector<std::string>{_columns});
for (const auto &c : cv){
columns->push_back(PColumnInfo(pop, c.first, c.second)); // emplace_back?
}
});
}
std::string tableName() const { return std::string(name.data()); }
ColumnType typeOfKey() const { return keyType.get_ro(); }
int findColumnByName(const std::string &colName) const;
ColumnType typeOfKey() const { return toColumnType<KeyType>(); }
const PColumnInfo &columnInfo(int pos) const { return columns->at(pos); }
std::size_t numColumns() const { return columns->size(); }
void setColumns(const ColumnVector vec);
PColumnIterator begin() const { return columns->begin(); }
PColumnIterator end() const { return columns->end(); }
};
void setColumns(const ColumnVector vec) {
auto pop = pool_by_vptr(this);
transaction::exec_tx(pop, [&] {
delete_persistent<PColumnVector>(columns);
columns = make_persistent<PColumnVector>();
for (const auto &c : vec)
columns->push_back(PColumnInfo(pop, c.first, c.second));
});
}
int findColumnByName(const std::string &colName) const {
for (std::size_t i = 0; i < (*columns).size(); i++) {
if (columns->at(i).getName() == colName) return (int) i;
}
return -1;
}
using PTableInfoPtr = persistent_ptr<PTableInfo>;
};
} /* namespace ptable */
......
......@@ -22,8 +22,8 @@
#include "DataNode.hpp"
using nvml::obj::persistent_ptr;
using nvml::obj::p;
using pmem::obj::persistent_ptr;
using pmem::obj::p;
namespace ptable {
......@@ -240,7 +240,7 @@ struct PTuplePrinter<Tuple, 0, KeyType> {
*
* // Insert into node and save the offsets ...
*
* PTuple<pfabric::Tuple<int, double, std::string>> ptp(node, tupleOffsets);
* PTuple<int, std::tuple<int, double, std::string>> ptp(node, tupleOffsets);
* \endcode
*
* Get reference to single attribute:
......@@ -254,7 +254,7 @@ struct PTuplePrinter<Tuple, 0, KeyType> {
* \note string attributes are returned as reference to a char array
* \author Philipp Goetze <philipp.goetze@tu-ilmenau.de>
*****************************************************************************/
template<class Tuple, typename KeyType>
template<typename KeyType, typename Tuple>
class PTuple {
public:
......@@ -286,8 +286,7 @@ class PTuple {
* the offsets for each tuple element
***************************************************************************/
PTuple(persistent_ptr<DataNode<KeyType>> _node, std::array<uint16_t, NUM_ATTRIBUTES> _offsets) :
node(_node), offsets(_offsets) {
}
node(_node), offsets(_offsets) {}
PTuple() : node(nullptr), offsets(std::array<uint16_t, std::tuple_size<Tuple>::value>()) {}
......@@ -300,10 +299,8 @@ class PTuple {
* a reference to the persistent tuple's attribute with the requested \c ID
***************************************************************************/
template<std::size_t ID>
typename getAttributeType<ID>::type &getAttribute() {
auto val = new typename getAttributeType<ID>::type;
*val = detail::get_helper<typename getAttributeType<ID>::type, ID, KeyType>::apply(node, offsets.get_ro().data());
return *val;
auto getAttribute() {
return this->get<ID>();
}
/************************************************************************//**
......@@ -328,8 +325,8 @@ class PTuple {
* a reference to the persistent tuple's attribute with the requested \c ID
***************************************************************************/
template<std::size_t ID>
const typename getAttributeType<ID>::type &getAttribute() const {
return detail::get_helper<typename getAttributeType<ID>::type, ID, KeyType>::apply(node, offsets.get_ro().data());
const auto getAttribute() const {
return this->get<ID>();
}
/************************************************************************//**
......@@ -399,8 +396,8 @@ class PTuple {
* \return
* a reference to the persistent tuple's attribute with the requested \c ID
*****************************************************************************/
template<std::size_t ID, class Tuple, typename KeyType>
auto get(const PTuple<Tuple, KeyType> &ptp) -> decltype((ptp.template get<ID>())) {
template<std::size_t ID, typename KeyType, typename Tuple>
auto get(const PTuple<KeyType, Tuple> &ptp) -> decltype((ptp.template get<ID>())) {
return ptp.template get<ID>();
}
......@@ -410,8 +407,8 @@ auto get(const PTuple<Tuple, KeyType> &ptp) -> decltype((ptp.template get<ID>())
* \param[in] os
* the output stream to print the tuple
*****************************************************************************/
template<class Tuple, typename KeyType>
void print(std::ostream &os, const PTuple<Tuple, KeyType> &ptp) {
template<typename KeyType, typename Tuple>
void print(std::ostream &os, const PTuple<KeyType, Tuple> &ptp) {