From 55e68401be875975951237e932d992db1c322c17 Mon Sep 17 00:00:00 2001
From: Tomasz Paluszkiewicz <tomaszp@man.poznan.pl>
Date: Thu, 27 Jun 2019 14:01:50 +0200
Subject: [PATCH 1/3] Split Data store from Context

---
 include/pdi/context.h                         |  71 +--
 include/pdi/context_proxy.h                   |  30 +-
 include/pdi/data_descriptor.h                 |  15 +-
 include/pdi/data_store.h                      | 134 +++++
 include/pdi/pdi_fwd.h                         |   4 +
 plugins/decl_hdf5/dataset_op.cxx              |   4 +-
 plugins/decl_hdf5/decl_hdf5.cxx               |   2 +-
 plugins/decl_sion/decl_sion.cxx               |  16 +-
 plugins/flowvr/src/module.h                   |  10 +-
 .../src/payloads/payload_button_event.h       |   4 +-
 plugins/flowvr/src/payloads/payload_chunk.h   |  30 +-
 plugins/flowvr/src/payloads/payload_data.h    |  40 +-
 .../flowvr/src/payloads/payload_mouse_event.h |   8 +-
 plugins/flowvr/src/port.h                     |   2 +-
 plugins/flowvr/src/stamps/stamp.h             |   2 +-
 plugins/flowvr/src/stamps/stamp_desc.h        |  10 +-
 plugins/flowvr/src/trace.h                    |   4 +-
 plugins/mpi/mpi.cxx                           |   4 +-
 plugins/mpi/mpi_comm_transtyper.h             |   4 +-
 plugins/pycall/pycall.cxx                     |   2 +-
 plugins/test/test.cxx                         |   2 +-
 plugins/trace/trace.cxx                       |   2 +-
 plugins/user_code/user_code.cxx               |   4 +-
 src/CMakeLists.txt                            |   2 +
 src/context.cxx                               |  56 +-
 src/context_proxy.cxx                         |  42 +-
 src/data_descriptor.cxx                       |  12 +
 src/data_descriptor_impl.cxx                  |  76 +--
 src/data_descriptor_impl.h                    |   8 +-
 src/data_store.cxx                            |  78 +++
 src/data_store_impl.cxx                       | 197 +++++++
 src/data_store_impl.h                         | 107 ++++
 src/expression.cxx                            |  10 +-
 src/global_context.cxx                        |  68 +--
 src/global_context.h                          |  66 +--
 src/pdi.cxx                                   |   9 +-
 src/python/pdi.cxx                            |   8 +-
 tests/CMakeLists.txt                          |   1 +
 tests/PDI_context.cxx                         | 469 ---------------
 tests/PDI_data_store.cxx                      | 534 ++++++++++++++++++
 tests/PDI_expression.cxx                      |  11 +-
 tests/mocks/context_mock.h                    |  23 +-
 tests/mocks/data_store_mock.h                 |  61 ++
 tests/mocks/global_context_mock.h             |  20 -
 44 files changed, 1271 insertions(+), 991 deletions(-)
 create mode 100644 include/pdi/data_store.h
 create mode 100644 src/data_store.cxx
 create mode 100644 src/data_store_impl.cxx
 create mode 100644 src/data_store_impl.h
 create mode 100644 tests/PDI_data_store.cxx
 create mode 100644 tests/mocks/data_store_mock.h

diff --git a/include/pdi/context.h b/include/pdi/context.h
index b2c5bdc1a..4fc93aa41 100644
--- a/include/pdi/context.h
+++ b/include/pdi/context.h
@@ -26,17 +26,12 @@
 #define PDI_CONTEXT_H_
 
 #include <functional>
-#include <list>
 #include <memory>
-#include <stack>
 #include <string>
-#include <unordered_map>
-#include <unordered_set>
 
 #include <pdi/pdi_fwd.h>
-#include <pdi/data_descriptor.h>
+#include <pdi/data_store.h>
 #include <pdi/datatype_template.h>
-#include <pdi/ref_any.h>
 
 
 namespace PDI {
@@ -44,57 +39,15 @@ namespace PDI {
 class PDI_EXPORT Context
 {
 public:
-	/** An iterator used to go through the descriptor store.
-	 */
-	class Iterator
-	{
-		friend class Context;
-		/// The iterator this wraps
-		std::unordered_map<std::string, std::unique_ptr<Data_descriptor>>::iterator m_data;
-		Iterator(const std::unordered_map<std::string, std::unique_ptr<Data_descriptor>>::iterator& data);
-		Iterator(std::unordered_map<std::string, std::unique_ptr<Data_descriptor>>::iterator&& data);
-	public:
-		Data_descriptor* operator-> ();
-		Data_descriptor& operator* ();
-		Iterator& operator++ ();
-		bool operator!= (const Iterator&);
-	};
-	
 	/** A function that parses a PC_tree_t to create a datatype_template
 	 */
 	typedef std::function<Datatype_template_uptr(Context&, PC_tree_t)> Datatype_template_parser;
 	
-protected:
-	Iterator get_iterator(const std::unordered_map<std::string, std::unique_ptr<Data_descriptor>>::iterator& data);
-	
-	Iterator get_iterator(std::unordered_map<std::string, std::unique_ptr<Data_descriptor>>::iterator&& data);
-	
-public:
 	virtual ~Context();
 	
-	/** Accesses the descriptor for a specific name. Might be uninitialized
-	 */
-	virtual Data_descriptor& desc(const std::string& name) = 0;
-	
-	/** Accesses the descriptor for a specific name. Might be uninitialized
-	 */
-	virtual Data_descriptor& desc(const char* name) = 0;
-	
-	/** Accesses the descriptor for a specific name. Might be uninitialized
-	 */
-	virtual Data_descriptor& operator[](const std::string& name) = 0;
-	
-	/** Accesses the descriptor for a specific name. Might be uninitialized
-	 */
-	virtual Data_descriptor& operator[](const char* name) = 0;
-	
-	/** Returns an iterator on the first descriptor
+	/** Access the data store of the context
 	 */
-	virtual Iterator begin() = 0;
-	
-	/** Returns an iterator past the last descriptor
-	 */
-	virtual Iterator end() = 0;
+	virtual Data_store& data() = 0;
 	
 	/** Triggers a PDI "event"
 	 * \param[in] name the event name
@@ -125,15 +78,6 @@ public:
 	 */
 	virtual std::function<void()> add_init_callback(const std::function<void()>& callback) = 0;
 	
-	/** Adds new data callback to context
-	 *
-	 * \param[in] callback function to call when data is being available
-	 * \param[in] name the name of the data on which call the callback, if not specified it's called on any data
-	 *
-	 * \return function that removes callback
-	 */
-	virtual std::function<void()> add_data_callback(const std::function<void(const std::string&, Ref)>& callback, const std::string& name = {}) = 0;
-	
 	/** Adds new event callback to context
 	 *
 	 * \param[in] callback function to call when event is called
@@ -143,15 +87,6 @@ public:
 	 */
 	virtual std::function<void()> add_event_callback(const std::function<void(const std::string&)>& callback, const std::string& name = {}) = 0;
 	
-	/** Adds new empty desc access callback to context
-	 *
-	 * \param[in] callback function to call when event is called
-	 * \param[in] name the name of the data on which call the callback, if not specified it's called on any data
-	 *
-	 * \return function that removes callback
-	 */
-	virtual std::function<void()> add_empty_desc_access_callback(const std::function<void(const std::string&)>& callback, const std::string& name = {}) = 0;
-	
 };
 
 } // namespace PDI
diff --git a/include/pdi/context_proxy.h b/include/pdi/context_proxy.h
index 1f7c0b0aa..637e7d634 100644
--- a/include/pdi/context_proxy.h
+++ b/include/pdi/context_proxy.h
@@ -25,11 +25,10 @@
 #ifndef PDI_CONTEXT_PROXY_H_
 #define PDI_CONTEXT_PROXY_H_
 
-#include "context.h"
 #include "pdi_fwd.h"
+#include "context.h"
 
 #include <string>
-#include <unordered_map>
 
 namespace PDI {
 
@@ -40,29 +39,7 @@ class PDI_EXPORT Context_proxy : public Context
 public:
 	Context_proxy(Context& ctx, std::string plugin_name, PC_tree_t logging_tree);
 	
-	/** Context::desc proxy for plugins
-	 */
-	Data_descriptor& desc(const std::string& name) override;
-	
-	/** Context::desc proxy for plugins
-	 */
-	Data_descriptor& desc(const char* name) override;
-	
-	/** Context::operator[] proxy for plugins
-	 */
-	Data_descriptor& operator[](const std::string& name) override;
-	
-	/** Context::operator[] proxy for plugins
-	 */
-	Data_descriptor& operator[](const char* name) override;
-	
-	/** Context::begin proxy for plugins
-	 */
-	Iterator begin() override;
-	
-	/** Context::end proxy for plugins
-	 */
-	Iterator end() override;
+	Data_store& data() override;
 	
 	/** Context::event proxy for plugins
 	 */
@@ -86,11 +63,8 @@ public:
 	
 	std::function<void()> add_init_callback(const std::function<void()>& callback) override;
 	
-	std::function<void()> add_data_callback(const std::function<void(const std::string&, Ref)>& callback, const std::string& name = {}) override;
-	
 	std::function<void()> add_event_callback(const std::function<void(const std::string&)>& callback, const std::string& name = {}) override;
 	
-	std::function<void()> add_empty_desc_access_callback(const std::function<void(const std::string&)>& callback, const std::string& name = {}) override;
 };
 
 } //namespace PDI
diff --git a/include/pdi/data_descriptor.h b/include/pdi/data_descriptor.h
index ec5f7458d..3af19680c 100644
--- a/include/pdi/data_descriptor.h
+++ b/include/pdi/data_descriptor.h
@@ -36,8 +36,21 @@ namespace PDI {
 
 class PDI_EXPORT Data_descriptor
 {
+protected:
+	/** Lets implementation of the Data_descriptor to call trigger_data_callbacks
+	 *  in Data_store class
+	 *
+	 * \param data_store the data store to trigger data callbacks on
+	 */
+	void trigger_data_callbacks(Data_store& data_store);
+	
+	/** Lets implementation of the Data_descriptor to call trigger_empty_desc_access_callbacks
+	 *  in Data_store class
+	 *
+	 * \param data_store the data store to trigger empty_desc_access callbacks on
+	 */
+	void trigger_empty_desc_access_callbacks(Data_store& data_store);
 public:
-
 	virtual ~Data_descriptor();
 	
 	/** Set the datatype template used to type raw pointers shared
diff --git a/include/pdi/data_store.h b/include/pdi/data_store.h
new file mode 100644
index 000000000..3e7219bb2
--- /dev/null
+++ b/include/pdi/data_store.h
@@ -0,0 +1,134 @@
+/*******************************************************************************
+ * Copyright (C) 2019 Commissariat a l'energie atomique et aux energies alternatives (CEA)
+ * Copyright (C) 2019 Institute of Bioorganic Chemistry Polish Academy of Science (PSNC)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * * Neither the name of CEA nor the names of its contributors may be used to
+ *   endorse or promote products derived from this software without specific
+ *   prior written permission.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ ******************************************************************************/
+
+#ifndef PDI_DATA_STORE_H_
+#define PDI_DATA_STORE_H_
+
+#include <functional>
+#include <list>
+#include <map>
+#include <memory>
+#include <string>
+#include <unordered_map>
+#include <unordered_set>
+
+#include <pdi/pdi_fwd.h>
+#include <pdi/data_descriptor.h>
+#include <pdi/datatype_template.h>
+#include <pdi/ref_any.h>
+
+
+namespace PDI {
+
+class PDI_EXPORT Data_store
+{
+public:
+	friend class Data_descriptor;
+	
+	/** An iterator used to go through the descriptor store.
+	 */
+	class Iterator
+	{
+		friend class Data_store;
+		/// The iterator this wraps
+		std::unordered_map<std::string, std::unique_ptr<Data_descriptor>>::iterator m_data;
+		Iterator(const std::unordered_map<std::string, std::unique_ptr<Data_descriptor>>::iterator& data);
+		Iterator(std::unordered_map<std::string, std::unique_ptr<Data_descriptor>>::iterator&& data);
+	public:
+		Data_descriptor* operator-> ();
+		Data_descriptor& operator* ();
+		Iterator& operator++ ();
+		bool operator!= (const Iterator&);
+	};
+	
+protected:
+	Iterator get_iterator(const std::unordered_map<std::string, std::unique_ptr<Data_descriptor>>::iterator& data);
+	
+	Iterator get_iterator(std::unordered_map<std::string, std::unique_ptr<Data_descriptor>>::iterator&& data);
+	
+private:
+	/** Triggers data callbacks
+	 *
+	 * \param[in] name name of the descriptor that triggers callbacks
+	 * \param[in] ref reference to the value of the data behind the descriptor
+	 */
+	virtual void trigger_data_callbacks(const std::string& name, Ref ref) = 0;
+	
+	/** Triggers empty_desc_access callbacks
+	 *
+	 * \param[in] name name of the descriptor that triggers callbacks
+	 */
+	virtual void trigger_empty_desc_access_callbacks(const std::string& name) = 0;
+	
+public:
+	virtual ~Data_store();
+	
+	/** Accesses the descriptor for a specific name. Might be uninitialized
+	 */
+	virtual Data_descriptor& desc(const std::string& name) = 0;
+	
+	/** Accesses the descriptor for a specific name. Might be uninitialized
+	 */
+	virtual Data_descriptor& desc(const char* name) = 0;
+	
+	/** Accesses the descriptor for a specific name. Might be uninitialized
+	 */
+	virtual Data_descriptor& operator[](const std::string& name) = 0;
+	
+	/** Accesses the descriptor for a specific name. Might be uninitialized
+	 */
+	virtual Data_descriptor& operator[](const char* name) = 0;
+	
+	/** Returns an iterator on the first descriptor
+	 */
+	virtual Iterator begin() = 0;
+	
+	/** Returns an iterator past the last descriptor
+	 */
+	virtual Iterator end() = 0;
+	
+	/** Adds new data callback to context
+	 *
+	 * \param[in] callback function to call when data is being available
+	 * \param[in] name the name of the data on which call the callback, if not specified it's called on any data
+	 *
+	 * \return function that removes callback
+	 */
+	virtual std::function<void()> add_data_callback(const std::function<void(const std::string&, Ref)>& callback, const std::string& name = {}) = 0;
+	
+	/** Adds new empty desc access callback to context
+	 *
+	 * \param[in] callback function to call when event is called
+	 * \param[in] name the name of the data on which call the callback, if not specified it's called on any data
+	 *
+	 * \return function that removes callback
+	 */
+	virtual std::function<void()> add_empty_desc_access_callback(const std::function<void(const std::string&)>& callback, const std::string& name = {}) = 0;
+	
+};
+
+} // namespace PDI
+
+#endif // PDI_DATA_STORE_H_
diff --git a/include/pdi/pdi_fwd.h b/include/pdi/pdi_fwd.h
index 34a47c00a..de465794d 100644
--- a/include/pdi/pdi_fwd.h
+++ b/include/pdi/pdi_fwd.h
@@ -61,6 +61,10 @@ typedef std::unique_ptr<Datatype_template> Datatype_template_uptr;
 
 typedef std::unique_ptr<Datatype> Datatype_uptr;
 
+/** Contains the set of data descriptors and descriptor related callbacks
+ */
+class Data_store;
+
 /** A data descriptors with a name and a value, it contains an implicit type
  * template that is used when exposing untyped data
  */
diff --git a/plugins/decl_hdf5/dataset_op.cxx b/plugins/decl_hdf5/dataset_op.cxx
index 7f6f25331..2044a6272 100644
--- a/plugins/decl_hdf5/dataset_op.cxx
+++ b/plugins/decl_hdf5/dataset_op.cxx
@@ -120,7 +120,7 @@ void Dataset_op::execute(Context& ctx, hid_t h5_file, hid_t xfer_lst, const unor
 
 void Dataset_op::do_read(Context& ctx, hid_t h5_file, hid_t read_lst)
 {
-	Ref_w ref = ctx[m_value].ref();
+	Ref_w ref = ctx.data()[m_value].ref();
 	if ( !ref ) {
 		ctx.logger()->warn("Reference to read not available: `{}'", m_value);
 		return;
@@ -144,7 +144,7 @@ void Dataset_op::do_read(Context& ctx, hid_t h5_file, hid_t read_lst)
 
 void Dataset_op::do_write(Context& ctx, hid_t h5_file, hid_t write_lst, const unordered_map<string, PDI::Datatype_template_uptr>& dsets)
 {
-	Ref_r ref = ctx[m_value].ref();
+	Ref_r ref = ctx.data()[m_value].ref();
 	if ( !ref ) {
 		ctx.logger()->warn("Reference to write not available: `{}'", m_value);
 		return;
diff --git a/plugins/decl_hdf5/decl_hdf5.cxx b/plugins/decl_hdf5/decl_hdf5.cxx
index f6afcd46c..992034510 100644
--- a/plugins/decl_hdf5/decl_hdf5.cxx
+++ b/plugins/decl_hdf5/decl_hdf5.cxx
@@ -116,7 +116,7 @@ public:
 			}
 		});
 		
-		ctx.add_data_callback([this](const std::string& name, Ref ref) {
+		ctx.data().add_data_callback([this](const std::string& name, Ref ref) {
 			this->data(name, ref);
 		});
 		ctx.add_event_callback([this](const std::string& name) {
diff --git a/plugins/decl_sion/decl_sion.cxx b/plugins/decl_sion/decl_sion.cxx
index dd5a535d1..f7c2fd3be 100644
--- a/plugins/decl_sion/decl_sion.cxx
+++ b/plugins/decl_sion/decl_sion.cxx
@@ -198,13 +198,13 @@ struct decl_sion_plugin: Plugin {
 		input_events{parse_events(PC_get(conf, ".inputs"))}
 	{
 		if (PC_status(conf)) throw Error {PDI_ERR_CONFIG, "Configuration is invalid"};
-		Data_descriptor& comm_desc = ctx.desc(comm_name);
+		Data_descriptor& comm_desc = ctx.data().desc(comm_name);
 		if ( !comm_desc.empty() ) {
 			if (MPI_Comm_dup(*(static_cast<const MPI_Comm*>(Ref_r{comm_desc.ref()}.get())), &comm)) {
 				throw Error {PDI_ERR_SYSTEM, "Cannot duplicate MPI communicator"};
 			}
 		}
-		ctx.add_data_callback([this](const std::string& name, Ref ref) {
+		ctx.data().add_data_callback([this](const std::string& name, Ref ref) {
 			this->data(name, ref);
 		});
 		
@@ -235,7 +235,7 @@ struct decl_sion_plugin: Plugin {
 	{
 		// check that data is available and data type is dense
 		for (auto&& var : event.vars) {
-			if (Ref_r ref = context().desc(var).ref()) {
+			if (Ref_r ref = context().data().desc(var).ref()) {
 				if (!ref.type().dense()) {
 					throw Error {PDI_ERR_IMPL, "Sparse data type of variable '%s' is not supported", var.c_str()};
 				}
@@ -248,7 +248,7 @@ struct decl_sion_plugin: Plugin {
 		
 		sion_int64 chunksize = 0;
 		for (auto&& var : event.vars) {
-			const Ref& ref = context().desc(var).ref();
+			const Ref& ref = context().data().desc(var).ref();
 			if (!ref) {
 				throw Error {PDI_ERR_RIGHT, "Dataset unavailable '%s'", var.c_str()};
 			}
@@ -261,7 +261,7 @@ struct decl_sion_plugin: Plugin {
 		int sid = sion_paropen_mpi(event.file.to_string(context()).c_str(), "w,keyval=inline", &n_files, comm, &comm, &chunksize, &blksize, &rank, NULL, NULL);
 		
 		for (auto&& var : event.vars) {
-			Ref_r ref = context().desc(var).ref();
+			Ref_r ref = context().data().desc(var).ref();
 			if (!ref) {
 				throw Error {PDI_ERR_RIGHT, "Dataset unavailable '%s'", var.c_str()};
 			}
@@ -300,7 +300,7 @@ struct decl_sion_plugin: Plugin {
 	{
 		// check that data type is dense
 		for (auto&& var : event.vars) {
-			Ref cref = context().desc(var).ref();
+			Ref cref = context().data().desc(var).ref();
 			if (Ref_w ref = cref) {
 				if (!ref.type().dense()) {
 					throw Error {PDI_ERR_IMPL, "Sparse data type of variable '%s' is not supported", var.c_str()};
@@ -319,7 +319,7 @@ struct decl_sion_plugin: Plugin {
 		int sid = sion_paropen_mpi(event.file.to_string(context()).c_str(), "r,keyval=unknown", &n_files, comm, &comm, &chunksize, &blksize, &rank, NULL, NULL);
 		
 		for (auto&& var : event.vars) {
-			Ref_w ref = context().desc(var).ref();
+			Ref_w ref = context().data().desc(var).ref();
 			if (!ref) {
 				throw Error {PDI_ERR_RIGHT, "Dataset unavailable '%s'", var.c_str()};
 			}
@@ -465,7 +465,7 @@ struct decl_sion_plugin: Plugin {
 	void data(const std::string& name, Ref cref)
 	{
 		if ( name == comm_name ) {
-			if (MPI_Comm_dup(*(static_cast<const MPI_Comm*>(Ref_r{context().desc(name).ref()}.get())), &comm)) {
+			if (MPI_Comm_dup(*(static_cast<const MPI_Comm*>(Ref_r{context().data().desc(name).ref()}.get())), &comm)) {
 				throw Error {PDI_ERR_SYSTEM, "Cannot duplicate MPI communicator"};
 			}
 		}
diff --git a/plugins/flowvr/src/module.h b/plugins/flowvr/src/module.h
index 3813775d4..be3e3cfde 100644
--- a/plugins/flowvr/src/module.h
+++ b/plugins/flowvr/src/module.h
@@ -83,7 +83,7 @@ private:
 		if (!PC_status(wait_on_data_node)) {
 			std::string wait_data = PDI::to_string(wait_on_data_node);
 			context().logger()->debug("(FlowVR) Module: wait_on_data = {}", wait_data);
-			context().add_data_callback([this](const std::string& name, PDI::Ref ref) {
+			context().data().add_data_callback([this](const std::string& name, PDI::Ref ref) {
 				this->wait(name, ref);
 			}, wait_data);
 		}
@@ -113,13 +113,13 @@ private:
 				int nb_status = PDI::len(status_node);
 				for (int status_id = 0; status_id < nb_status; status_id++) {
 					std::string status_data = PDI::to_string(PC_get(status_node, "[%d]", status_id));
-					context().add_data_callback([this](const std::string& name, PDI::Ref ref) {
+					context().data().add_data_callback([this](const std::string& name, PDI::Ref ref) {
 						this->status(ref);
 					}, status_data);
 				}
 			} else {
 				std::string status_data = PDI::to_string(status_node);
-				context().add_data_callback([this](const std::string& name, PDI::Ref ref) {
+				context().data().add_data_callback([this](const std::string& name, PDI::Ref ref) {
 					this->status(ref);
 				}, status_data);
 			}
@@ -150,7 +150,7 @@ private:
 			if (!PC_status(get_rank_node)) {
 				std::string get_parallel_rank_data = PDI::to_string(get_rank_node);
 				context().logger()->debug("(FlowVR) Module: Parallel rank = {}", get_parallel_rank_data);
-				context().add_data_callback([this](const std::string& name, PDI::Ref ref) {
+				context().data().add_data_callback([this](const std::string& name, PDI::Ref ref) {
 					this->get_parallel_rank(name, ref);
 				}, get_parallel_rank_data);
 			}
@@ -159,7 +159,7 @@ private:
 			if (!PC_status(get_size_node)) {
 				std::string get_parallel_size_data = PDI::to_string(get_size_node);
 				context().logger()->debug("(FlowVR) Module: Parallel size = {}", get_parallel_size_data);
-				context().add_data_callback([this](const std::string& name, PDI::Ref ref) {
+				context().data().add_data_callback([this](const std::string& name, PDI::Ref ref) {
 					this->get_parallel_size(name, ref);
 				}, get_parallel_size_data);
 			}
diff --git a/plugins/flowvr/src/payloads/payload_button_event.h b/plugins/flowvr/src/payloads/payload_button_event.h
index 55fb34f25..c16acff4f 100644
--- a/plugins/flowvr/src/payloads/payload_button_event.h
+++ b/plugins/flowvr/src/payloads/payload_button_event.h
@@ -146,7 +146,7 @@ public:
 		m_flowvr_input_port{parent_port}
 	{
 		for (const auto& desc_value : m_desc_value_map) {
-			m_ctx.add_data_callback([this](const std::string& name, PDI::Ref ref) {
+			m_ctx.data().add_data_callback([this](const std::string& name, PDI::Ref ref) {
 				this->data(name, ref);
 			}, desc_value.first);
 		}
@@ -235,7 +235,7 @@ public:
 		m_flowvr_output_port{parent_port}
 	{
 		for (const auto& desc_value : m_desc_value_map) {
-			m_ctx.add_data_callback([this](const std::string& name, PDI::Ref ref) {
+			m_ctx.data().add_data_callback([this](const std::string& name, PDI::Ref ref) {
 				this->data(name, ref);
 			}, desc_value.first);
 		}
diff --git a/plugins/flowvr/src/payloads/payload_chunk.h b/plugins/flowvr/src/payloads/payload_chunk.h
index b59e3fa04..f77930e9b 100644
--- a/plugins/flowvr/src/payloads/payload_chunk.h
+++ b/plugins/flowvr/src/payloads/payload_chunk.h
@@ -77,7 +77,7 @@ protected:
 			m_chunk_descs.emplace_back(PDI::to_string(PC_get(chunk_node, ".data")));
 		}
 		for (const std::string& data_name : m_chunk_descs) {
-			m_ctx.add_empty_desc_access_callback([this](const std::string& name) {
+			m_ctx.data().add_empty_desc_access_callback([this](const std::string& name) {
 				this->empty_desc_access(name);
 			}, data_name);
 		}
@@ -178,8 +178,8 @@ public:
 			long size = static_cast<long>(m_chunk_info.chunks_sizes[chunk_id]);
 			if (!chunk_size_desc_it->second.empty()) {
 				m_ctx.logger()->debug("(FlowVR) Input Chunk Payload ({}): Sharing size descriptor `{}' = {}", m_name, data_size_desc, size);
-				m_ctx[data_size_desc].share(&size, true, false);
-				m_ctx[data_size_desc].reclaim();
+				m_ctx.data()[data_size_desc].share(&size, true, false);
+				m_ctx.data()[data_size_desc].reclaim();
 				m_ctx.logger()->debug("(FlowVR) Input Chunk Payload ({}): Share size descriptor complete ", m_name);
 			}
 			
@@ -194,13 +194,13 @@ public:
 			if (m_flowvr_buffer.unique(flowvr::Buffer::ALLSEGMENTS) ) {
 				m_ctx.logger()->debug("(FlowVR) Input Chunk Payload ({}): Share (read/write) `{}'", m_name, chunk_desc);
 				m_sharing_chunks = true;
-				m_ctx[chunk_desc].share(const_cast<flowvr::ubyte*>(m_flowvr_buffer.readAccess() + offset), true, true);
+				m_ctx.data()[chunk_desc].share(const_cast<flowvr::ubyte*>(m_flowvr_buffer.readAccess() + offset), true, true);
 				m_ctx.logger()->debug("(FlowVR) Input Chunk Payload ({}): Share (read/write) complete `{}'", m_name, chunk_desc);
 			} else {
 				if (m_flowvr_buffer.valid()) {
 					m_ctx.logger()->debug("(FlowVR) Input Chunk Payload ({}): Share (read only) `{}'", m_name, chunk_desc);
 					m_sharing_chunks = true;
-					m_ctx[chunk_desc].share(const_cast<flowvr::ubyte*>(m_flowvr_buffer.readAccess() + offset), true, false);
+					m_ctx.data()[chunk_desc].share(const_cast<flowvr::ubyte*>(m_flowvr_buffer.readAccess() + offset), true, false);
 					m_ctx.logger()->debug("(FlowVR) Input Chunk Payload ({}): Share (read only) complete `{}'", m_name, chunk_desc);
 				}
 			}
@@ -208,7 +208,7 @@ public:
 			m_ctx.logger()->warn("(FlowVR) Input Chunk Payload ({}): Flowvr data `{}' is empty or not valid", m_name, chunk_desc);
 			m_ctx.logger()->debug("(FlowVR) Input Chunk Payload ({}): Sharing (nullptr) `{}'", m_name, chunk_desc);
 			m_sharing_chunks = true;
-			m_ctx[chunk_desc].share(PDI::Ref(nullptr, nullptr, PDI::UNDEF_TYPE.clone_type(), true, false), false, false);
+			m_ctx.data()[chunk_desc].share(PDI::Ref(nullptr, nullptr, PDI::UNDEF_TYPE.clone_type(), true, false), false, false);
 			m_ctx.logger()->debug("(FlowVR) Input Chunk Payload ({}): Share (nullptr) complete `{}'", m_name, chunk_desc);
 		}
 	}
@@ -222,9 +222,9 @@ public:
 	{
 		for (std::string chunk_desc : m_chunk_descs) {
 			if (!chunk_desc.empty()) {
-				if (!m_ctx[chunk_desc].empty()) {
+				if (!m_ctx.data()[chunk_desc].empty()) {
 					m_ctx.logger()->debug("(FlowVR) Input Chunk Payload ({}): Reclaiming {}", m_name, chunk_desc);
-					m_ctx[chunk_desc].reclaim(); // have to reclaim (this is flowvr shared buffer)
+					m_ctx.data()[chunk_desc].reclaim(); // have to reclaim (this is flowvr shared buffer)
 					m_sharing_chunks = false;
 				}
 			}
@@ -248,9 +248,9 @@ public:
 	~Input_payload_chunk()
 	{
 		for (std::string chunk_desc : m_chunk_descs) {
-			if (!m_ctx[chunk_desc].empty() && m_sharing_chunks) {
+			if (!m_ctx.data()[chunk_desc].empty() && m_sharing_chunks) {
 				m_ctx.logger()->debug("(FlowVR) Input Chunk Payload ({}): Destroing chunk, reclaiming {}", m_name, chunk_desc);
-				m_ctx[chunk_desc].reclaim(); // have to reclaim (this is flowvr shared buffer)
+				m_ctx.data()[chunk_desc].reclaim(); // have to reclaim (this is flowvr shared buffer)
 				m_sharing_chunks = false;
 			}
 		}
@@ -303,7 +303,7 @@ public:
 			m_chunk_info.chunks_sizes.reset(new size_t[m_chunk_info.chunk_count]);
 			size_t buffersize = sizeof(size_t) * (1 + m_chunk_info.chunk_count) ; //m_chunk_info
 			for (int i = 0; i < m_chunk_info.chunk_count; i++) {
-				m_chunk_info.chunks_sizes[i] = m_ctx[m_chunk_descs[i]].default_type()->evaluate(m_ctx)->buffersize();
+				m_chunk_info.chunks_sizes[i] = m_ctx.data()[m_chunk_descs[i]].default_type()->evaluate(m_ctx)->buffersize();
 				buffersize += m_chunk_info.chunks_sizes[i];
 			}
 			m_flowvr_buffer = m_parent_port->getModule()->alloc(buffersize);
@@ -327,7 +327,7 @@ public:
 		
 		m_ctx.logger()->debug("(FlowVR) Output Chunk Payload ({}): Sharing `{}'", m_name, data_name);
 		m_sharing_chunks = true;
-		m_ctx[data_name].share(m_flowvr_buffer.writeAccess() + offset, false, true);
+		m_ctx.data()[data_name].share(m_flowvr_buffer.writeAccess() + offset, false, true);
 		m_ctx.logger()->debug("(FlowVR) Output Chunk Payload ({}): Share complete `{}'", m_name, data_name);
 	}
 	
@@ -355,7 +355,7 @@ public:
 		for (std::string chunk_desc : m_chunk_descs) {
 			if (!chunk_desc.empty()) {
 				m_ctx.logger()->debug("(FlowVR) Output Chunk Payload ({}): Reclaiming `{}'", m_name, chunk_desc);
-				m_ctx[chunk_desc].reclaim(); // have to reclaim (this is flowvr shared buffer)
+				m_ctx.data()[chunk_desc].reclaim(); // have to reclaim (this is flowvr shared buffer)
 				m_sharing_chunks = false;
 			}
 		}
@@ -366,9 +366,9 @@ public:
 	~Output_payload_chunk()
 	{
 		for (std::string chunk_desc : m_chunk_descs) {
-			if (!m_ctx[chunk_desc].empty() && m_sharing_chunks) {
+			if (!m_ctx.data()[chunk_desc].empty() && m_sharing_chunks) {
 				m_ctx.logger()->debug("(FlowVR) Output Chunk Payload ({}): Destroing payload, reclaiming {}", m_name, chunk_desc);
-				m_ctx[chunk_desc].reclaim(); // have to reclaim (this is flowvr shared buffer)
+				m_ctx.data()[chunk_desc].reclaim(); // have to reclaim (this is flowvr shared buffer)
 				m_sharing_chunks = false;
 			}
 		}
diff --git a/plugins/flowvr/src/payloads/payload_data.h b/plugins/flowvr/src/payloads/payload_data.h
index 430dd1045..4f078f4f5 100644
--- a/plugins/flowvr/src/payloads/payload_data.h
+++ b/plugins/flowvr/src/payloads/payload_data.h
@@ -120,10 +120,10 @@ public:
 	{
 		load_data_size_desc(config);
 		if (!m_data_desc.empty()) {
-			m_ctx.add_data_callback([this](const std::string& name, PDI::Ref ref) {
+			m_ctx.data().add_data_callback([this](const std::string& name, PDI::Ref ref) {
 				this->copy_data_to_ref(name, ref);
 			}, m_data_desc);
-			m_ctx.add_empty_desc_access_callback([this](const std::string& name) {
+			m_ctx.data().add_empty_desc_access_callback([this](const std::string& name) {
 				this->empty_desc_access(name);
 			}, m_data_desc);
 		}
@@ -165,7 +165,7 @@ public:
 					if (m_data_selection) {
 						m_data_selection->evaluate(m_ctx)->data_from_dense_copy(ref.get(), m_flowvr_buffer.readAccess());
 					} else {
-						m_ctx[m_data_desc].default_type()->evaluate(m_ctx)->data_from_dense_copy(ref.get(), m_flowvr_buffer.readAccess());
+						m_ctx.data()[m_data_desc].default_type()->evaluate(m_ctx)->data_from_dense_copy(ref.get(), m_flowvr_buffer.readAccess());
 					}
 				}
 			} else {
@@ -185,13 +185,13 @@ public:
 			if (m_flowvr_buffer.unique(flowvr::Buffer::ALLSEGMENTS)) {
 				m_ctx.logger()->debug("(FlowVR) Input Data Payload ({}): Sharing (read/write) `{}'", m_name, m_data_desc);
 				m_sharing_buffer = true;
-				m_ctx[m_data_desc].share(const_cast<flowvr::ubyte*>(m_flowvr_buffer.readAccess()), true, true);
+				m_ctx.data()[m_data_desc].share(const_cast<flowvr::ubyte*>(m_flowvr_buffer.readAccess()), true, true);
 				m_ctx.logger()->debug("(FlowVR) Input Data Payload ({}): Share complete (read/write) `{}'", m_name, m_data_desc);
 			} else {
 				if (m_flowvr_buffer.valid()) {
 					m_ctx.logger()->debug("(FlowVR) Input Data Payload ({}): Share (read only) `{}'", m_name, m_data_desc);
 					m_sharing_buffer = true;
-					m_ctx[m_data_desc].share(const_cast<flowvr::ubyte*>(m_flowvr_buffer.readAccess()), true, false);
+					m_ctx.data()[m_data_desc].share(const_cast<flowvr::ubyte*>(m_flowvr_buffer.readAccess()), true, false);
 					m_ctx.logger()->debug("(FlowVR) Input Data Payload ({}): Share complete (read only) `{}'", m_name, m_data_desc);
 				}
 			}
@@ -199,7 +199,7 @@ public:
 			m_ctx.logger()->warn("(FlowVR) Input Data Payload ({}): Flowvr data {} is empty or not valid", m_name, m_data_desc);
 			m_ctx.logger()->debug("(FlowVR) Input Data Payload ({}): Sharing (nullptr) `{}'", m_name, m_data_desc);
 			m_sharing_buffer = true;
-			m_ctx[m_data_desc].share(PDI::Ref(nullptr, nullptr, PDI::UNDEF_TYPE.clone_type(), true, false), false, false);
+			m_ctx.data()[m_data_desc].share(PDI::Ref(nullptr, nullptr, PDI::UNDEF_TYPE.clone_type(), true, false), false, false);
 			m_ctx.logger()->debug("(FlowVR) Input Data Payload ({}): Share complete (nullptr) `{}'", m_name, m_data_desc);
 		}
 	}
@@ -212,9 +212,9 @@ public:
 	flowvr::Stamps get_message() override
 	{
 		if (!m_data_desc.empty()) {
-			if (!m_ctx[m_data_desc].empty() && m_sharing_buffer) {
+			if (!m_ctx.data()[m_data_desc].empty() && m_sharing_buffer) {
 				m_ctx.logger()->debug("(FlowVR) Input Data Payload ({}): Reclaiming `{}'", m_name, m_data_desc);
-				m_ctx[m_data_desc].reclaim(); // have to reclaim (this is flowvr shared buffer)
+				m_ctx.data()[m_data_desc].reclaim(); // have to reclaim (this is flowvr shared buffer)
 				m_sharing_buffer = false;
 				m_ctx.logger()->debug("(FlowVR) Input Data Payload ({}): Reclaim complete `{}'", m_name, m_data_desc);
 			}
@@ -232,8 +232,8 @@ public:
 				//update size in metadata
 				long size = m_flowvr_buffer.getSize();
 				m_ctx.logger()->debug("(FlowVR) Input Data Payload ({}): Sharing size descripotr `{}' = {}", m_name, m_data_size_desc, size);
-				m_ctx[m_data_size_desc].share(&size, true, false);
-				m_ctx[m_data_size_desc].reclaim();
+				m_ctx.data()[m_data_size_desc].share(&size, true, false);
+				m_ctx.data()[m_data_size_desc].reclaim();
 				m_ctx.logger()->debug("(FlowVR) Input Data Payload ({}): Share complete `{}'", m_name, m_data_size_desc);
 			}
 		}
@@ -242,10 +242,10 @@ public:
 	
 	~Input_payload_data()
 	{
-		if (!m_ctx[m_data_desc].empty()) {
+		if (!m_ctx.data()[m_data_desc].empty()) {
 			if (m_sharing_buffer) {
 				m_ctx.logger()->debug("(FlowVR) Input Data Payload ({}): Reclaiming {}", m_name, m_data_desc);
-				m_ctx[m_data_desc].reclaim(); // have to reclaim (this is flowvr shared buffer)
+				m_ctx.data()[m_data_desc].reclaim(); // have to reclaim (this is flowvr shared buffer)
 				m_sharing_buffer = false;
 				m_ctx.logger()->debug("(FlowVR) Input Data Payload ({}): Reclaim complete {}", m_name, m_data_desc);
 			}
@@ -269,7 +269,7 @@ class Output_payload_data : public Output_payload, public Payload_data
 		if (m_data_selection) {
 			datasize = m_data_selection->evaluate(m_ctx)->datasize();
 		} else {
-			datasize = m_ctx[m_data_desc].default_type()->evaluate(m_ctx)->datasize();
+			datasize = m_ctx.data()[m_data_desc].default_type()->evaluate(m_ctx)->datasize();
 		}
 		m_ctx.logger()->debug("(FlowVR) Output Data Payload ({}): Allocating {} B", m_name, datasize);
 		if (m_flowvr_buffer_pool) {
@@ -295,10 +295,10 @@ public:
 		}
 		
 		if (!m_data_desc.empty()) {
-			m_ctx.add_data_callback([this](const std::string& name, PDI::Ref ref) {
+			m_ctx.data().add_data_callback([this](const std::string& name, PDI::Ref ref) {
 				this->copy_data_from_ref(name, ref);
 			}, m_data_desc);
-			m_ctx.add_empty_desc_access_callback([this](const std::string& name) {
+			m_ctx.data().add_empty_desc_access_callback([this](const std::string& name) {
 				this->empty_desc_access(name);
 			}, m_data_desc);
 		}
@@ -344,7 +344,7 @@ public:
 						if (m_data_selection) {
 							m_data_selection->evaluate(m_ctx)->data_to_dense_copy(m_flowvr_buffer.writeAccess(), ref.get());
 						} else {
-							m_ctx[m_data_desc].default_type()->evaluate(m_ctx)->data_to_dense_copy(m_flowvr_buffer.writeAccess(), ref.get());
+							m_ctx.data()[m_data_desc].default_type()->evaluate(m_ctx)->data_to_dense_copy(m_flowvr_buffer.writeAccess(), ref.get());
 						}
 					}
 				}
@@ -366,7 +366,7 @@ public:
 		}
 		m_ctx.logger()->debug("(FlowVR) Output Data Payload ({}): Sharing `{}'", m_name, m_data_desc);
 		m_sharing_buffer = true; //must be before share (to not copy data)
-		m_ctx[data_name].share(m_flowvr_buffer.writeAccess(), false, true);
+		m_ctx.data()[data_name].share(m_flowvr_buffer.writeAccess(), false, true);
 		m_ctx.logger()->debug("(FlowVR) Output Data Payload ({}): Share complete `{}'", m_name, m_data_desc);
 	}
 	
@@ -395,7 +395,7 @@ public:
 		}
 		if (m_sharing_buffer) {
 			m_ctx.logger()->debug("(FlowVR) Output Data Payload ({}): Reclaiming `{}'", m_name, m_data_desc);
-			m_ctx[m_data_desc].reclaim(); // have to reclaim (this is flowvr shared buffer)
+			m_ctx.data()[m_data_desc].reclaim(); // have to reclaim (this is flowvr shared buffer)
 			m_sharing_buffer = false;
 			m_ctx.logger()->debug("(FlowVR) Output Data Payload ({}): Reclaim complete `{}'", m_name, m_data_desc);
 		}
@@ -404,10 +404,10 @@ public:
 	
 	~Output_payload_data()
 	{
-		if (!m_ctx[m_data_desc].empty()) {
+		if (!m_ctx.data()[m_data_desc].empty()) {
 			if (m_sharing_buffer) {
 				m_ctx.logger()->debug("(FlowVR) Output Data Payload ({}): Reclaiming `{}'", m_name, m_data_desc);
-				m_ctx[m_data_desc].reclaim(); // have to reclaim (this is flowvr shared buffer)
+				m_ctx.data()[m_data_desc].reclaim(); // have to reclaim (this is flowvr shared buffer)
 				m_sharing_buffer = false;
 				m_ctx.logger()->debug("(FlowVR) Output Data Payload ({}): Reclaim complete `{}'", m_name, m_data_desc);
 			}
diff --git a/plugins/flowvr/src/payloads/payload_mouse_event.h b/plugins/flowvr/src/payloads/payload_mouse_event.h
index f045955b1..0247f4357 100644
--- a/plugins/flowvr/src/payloads/payload_mouse_event.h
+++ b/plugins/flowvr/src/payloads/payload_mouse_event.h
@@ -143,11 +143,11 @@ public:
 		Payload_mouse_event{ctx, name, config, parent_port},
 		m_flowvr_input_port{parent_port}
 	{
-		m_ctx.add_data_callback([this](const std::string& name, PDI::Ref ref) {
+		m_ctx.data().add_data_callback([this](const std::string& name, PDI::Ref ref) {
 			this->data_pos_xy(name, ref);
 		}, m_desc_pos_xy.first);
 		for (const auto& desc_value : m_desc_value_map) {
-			m_ctx.add_data_callback([this](const std::string& name, PDI::Ref ref) {
+			m_ctx.data().add_data_callback([this](const std::string& name, PDI::Ref ref) {
 				this->data(name, ref);
 			}, desc_value.first);
 		}
@@ -268,11 +268,11 @@ public:
 		Payload_mouse_event{ctx, name, config, parent_port},
 		m_flowvr_output_port{parent_port}
 	{
-		m_ctx.add_data_callback([this](const std::string& name, PDI::Ref ref) {
+		m_ctx.data().add_data_callback([this](const std::string& name, PDI::Ref ref) {
 			this->data_pos_xy(name, ref);
 		}, m_desc_pos_xy.first);
 		for (const auto& desc_value : m_desc_value_map) {
-			m_ctx.add_data_callback([this](const std::string& name, PDI::Ref ref) {
+			m_ctx.data().add_data_callback([this](const std::string& name, PDI::Ref ref) {
 				this->data(name, ref);
 			}, desc_value.first);
 		}
diff --git a/plugins/flowvr/src/port.h b/plugins/flowvr/src/port.h
index ec18b2639..37558f9b2 100644
--- a/plugins/flowvr/src/port.h
+++ b/plugins/flowvr/src/port.h
@@ -63,7 +63,7 @@ protected:
 	{
 		PC_tree_t isConnected_node = PC_get(config, ".isConnected");
 		if (!PC_status(isConnected_node)) {
-			m_ctx.add_data_callback([this](const std::string& name, PDI::Ref ref) {
+			m_ctx.data().add_data_callback([this](const std::string& name, PDI::Ref ref) {
 				this->get_status(name, ref);
 			}, PDI::to_string(isConnected_node));
 		}
diff --git a/plugins/flowvr/src/stamps/stamp.h b/plugins/flowvr/src/stamps/stamp.h
index fdf824049..623f5077d 100644
--- a/plugins/flowvr/src/stamps/stamp.h
+++ b/plugins/flowvr/src/stamps/stamp.h
@@ -62,7 +62,7 @@ class Stamp
 	{
 		size_t stamp_size = 1; //default for scalars
 		
-		const PDI::Datatype_uptr stamp_datatype = m_ctx[data_desc].default_type()->evaluate(m_ctx);
+		const PDI::Datatype_uptr stamp_datatype = m_ctx.data()[data_desc].default_type()->evaluate(m_ctx);
 		const PDI::Scalar_datatype* scalar_datatype = dynamic_cast<PDI::Scalar_datatype*>(stamp_datatype.get());
 		const PDI::Array_datatype* array_datatype = dynamic_cast<PDI::Array_datatype*>(stamp_datatype.get());
 		
diff --git a/plugins/flowvr/src/stamps/stamp_desc.h b/plugins/flowvr/src/stamps/stamp_desc.h
index 6e4ce2cf0..45ff82d76 100644
--- a/plugins/flowvr/src/stamps/stamp_desc.h
+++ b/plugins/flowvr/src/stamps/stamp_desc.h
@@ -58,7 +58,7 @@ public:
 		} else {
 			m_stamp_info = new flowvr::StampInfo(m_name, flowvr::TypeInt::create());
 		}
-		m_ctx.add_data_callback([this](const std::string& name, PDI::Ref ref) {
+		m_ctx.data().add_data_callback([this](const std::string& name, PDI::Ref ref) {
 			this->data(name, ref);
 		}, data_desc);
 		m_ctx.logger()->debug("(FlowVR) Int STAMP ({}): Created", m_name);
@@ -109,7 +109,7 @@ public:
 		Stamp_base{ctx, parent_port, name}
 	{
 		m_stamp_info = new flowvr::StampInfo(m_name, flowvr::TypeFloat::create());
-		m_ctx.add_data_callback([this](const std::string& name, PDI::Ref ref) {
+		m_ctx.data().add_data_callback([this](const std::string& name, PDI::Ref ref) {
 			this->data(name, ref);
 		}, data_desc);
 		m_ctx.logger()->debug("(FlowVR) Float STAMP ({}): Created", m_name);
@@ -164,7 +164,7 @@ public:
 		} else {
 			m_stamp_info = new flowvr::StampInfo(m_name, flowvr::TypeString::create());
 		}
-		m_ctx.add_data_callback([this](const std::string& name, PDI::Ref ref) {
+		m_ctx.data().add_data_callback([this](const std::string& name, PDI::Ref ref) {
 			this->data(name, ref);
 		}, data_desc);
 		m_ctx.logger()->debug("(FlowVR) String STAMP ({}): Created", m_name);
@@ -216,7 +216,7 @@ public:
 		m_value(size)
 	{
 		m_stamp_info = new flowvr::StampInfo(m_name, flowvr::TypeArray::create(size, flowvr::TypeInt::create()));
-		m_ctx.add_data_callback([this](const std::string& name, PDI::Ref ref) {
+		m_ctx.data().add_data_callback([this](const std::string& name, PDI::Ref ref) {
 			this->data(name, ref);
 		}, data_desc);
 		m_ctx.logger()->debug("(FlowVR) Int array STAMP ({}): Created with size = {}", m_name, size);
@@ -272,7 +272,7 @@ public:
 		m_value(size)
 	{
 		m_stamp_info = new flowvr::StampInfo(m_name, flowvr::TypeArray::create(size, flowvr::TypeFloat::create()));
-		m_ctx.add_data_callback([this](const std::string& name, PDI::Ref ref) {
+		m_ctx.data().add_data_callback([this](const std::string& name, PDI::Ref ref) {
 			this->data(name, ref);
 		}, data_desc);
 		m_ctx.logger()->debug("(FlowVR) Float array STAMP ({}): Created with size = {}", m_name, size);
diff --git a/plugins/flowvr/src/trace.h b/plugins/flowvr/src/trace.h
index c2f5f441f..180004263 100644
--- a/plugins/flowvr/src/trace.h
+++ b/plugins/flowvr/src/trace.h
@@ -65,7 +65,7 @@ class Trace
 	 */
 	void load_trace(PDI::Context& ctx, std::string name)
 	{
-		const PDI::Datatype_uptr trace_datatype = ctx[m_on_data].default_type()->evaluate(ctx);
+		const PDI::Datatype_uptr trace_datatype = ctx.data()[m_on_data].default_type()->evaluate(ctx);
 		const PDI::Scalar_datatype* scalar_datatype = dynamic_cast<PDI::Scalar_datatype*>(trace_datatype.get());
 		const PDI::Array_datatype* array_datatype = dynamic_cast<PDI::Array_datatype*>(trace_datatype.get());
 		
@@ -93,7 +93,7 @@ public:
 	{
 		load_on_data(config);
 		load_trace(ctx, name);
-		ctx.add_data_callback([this](const std::string& name, PDI::Ref ref) {
+		ctx.data().add_data_callback([this](const std::string& name, PDI::Ref ref) {
 			this->write(name, ref);
 		}, m_on_data);
 		
diff --git a/plugins/mpi/mpi.cxx b/plugins/mpi/mpi.cxx
index 4d2989699..b02fa9b42 100644
--- a/plugins/mpi/mpi.cxx
+++ b/plugins/mpi/mpi.cxx
@@ -74,7 +74,7 @@ struct mpi_plugin: Plugin {
 	
 	void add_predefined(Context& ctx, const string& name, void* data, Datatype_uptr type)
 	{
-		Data_descriptor& predef_desc = ctx.desc(name);
+		Data_descriptor& predef_desc = ctx.data().desc(name);
 		if (!predef_desc.empty()) {
 			throw Error{PDI_ERR_IMPL, "Predefined descriptor already defined `%s'", name.c_str()};
 		}
@@ -132,7 +132,7 @@ struct mpi_plugin: Plugin {
 		MPI_Fint comm_null_f = MPI_Comm_c2f(MPI_COMM_NULL);
 		add_predefined(ctx, "MPI_COMM_NULL_F", &comm_null_f, mpi_comm_f_datatype.clone_type());
 		
-		ctx.add_data_callback([this](const std::string& name, PDI::Ref ref) {
+		ctx.data().add_data_callback([this](const std::string& name, PDI::Ref ref) {
 			this->m_transtyper.data(name.c_str(), ref);
 		});
 		
diff --git a/plugins/mpi/mpi_comm_transtyper.h b/plugins/mpi/mpi_comm_transtyper.h
index 4a2e0dfa1..39c19ac33 100644
--- a/plugins/mpi/mpi_comm_transtyper.h
+++ b/plugins/mpi/mpi_comm_transtyper.h
@@ -150,8 +150,8 @@ public:
         auto&& descs_it = m_descs.find(name);
 		if (descs_it == m_descs.end()) return;
 
-        auto&& source_desc = m_ctx[descs_it->first];
-        auto&& transtype_desc = m_ctx[descs_it->second];
+        auto&& source_desc = m_ctx.data()[descs_it->first];
+        auto&& transtype_desc = m_ctx.data()[descs_it->second];
         auto&& source_type_uptr = source_desc.default_type()->evaluate(m_ctx);
         auto&& transtype_type_uptr = transtype_desc.default_type()->evaluate(m_ctx);
 
diff --git a/plugins/pycall/pycall.cxx b/plugins/pycall/pycall.cxx
index 7ae6d8f63..bccbee6d4 100644
--- a/plugins/pycall/pycall.cxx
+++ b/plugins/pycall/pycall.cxx
@@ -92,7 +92,7 @@ public:
 	{
 		Ref r = m_value.to_ref(ctx);
 		
-		PDI::Data_descriptor& desc = ctx.desc(m_name);
+		PDI::Data_descriptor& desc = ctx.data().desc(m_name);
 		
 		pyscope[m_name.c_str()] = to_python(r);
 	}
diff --git a/plugins/test/test.cxx b/plugins/test/test.cxx
index 33a4b2f20..b38d0e70d 100644
--- a/plugins/test/test.cxx
+++ b/plugins/test/test.cxx
@@ -54,7 +54,7 @@ struct test_plugin: Plugin {
 	test_plugin(Context& ctx, PC_tree_t):
 		Plugin {ctx}
 	{
-		ctx.add_data_callback([this](const std::string& name, Ref ref) {
+		ctx.data().add_data_callback([this](const std::string& name, Ref ref) {
 			this->data(name, ref);
 		});
 		ctx.add_event_callback([this](const std::string& name) {
diff --git a/plugins/trace/trace.cxx b/plugins/trace/trace.cxx
index 5f6aeacf0..4b101c9aa 100644
--- a/plugins/trace/trace.cxx
+++ b/plugins/trace/trace.cxx
@@ -54,7 +54,7 @@ struct trace_plugin: Plugin {
 	trace_plugin(Context& ctx, PC_tree_t):
 		Plugin {ctx}
 	{
-		ctx.add_data_callback([this](const std::string& name, Ref ref) {
+		ctx.data().add_data_callback([this](const std::string& name, Ref ref) {
 			this->data(name, ref);
 		});
 		ctx.add_event_callback([this](const std::string& name) {
diff --git a/plugins/user_code/user_code.cxx b/plugins/user_code/user_code.cxx
index 905fbeb51..91bfe4cca 100644
--- a/plugins/user_code/user_code.cxx
+++ b/plugins/user_code/user_code.cxx
@@ -111,7 +111,7 @@ public:
 }; // class Alias
 
 ExposedAlias::ExposedAlias(Context& ctx, const Alias& alias):
-	m_desc{&ctx.desc(alias.m_name.c_str())}
+	m_desc{&ctx.data().desc(alias.m_name.c_str())}
 {
 	try {
 		m_desc->share(alias.m_value.to_ref(ctx), false, false);
@@ -209,7 +209,7 @@ struct user_code_plugin: Plugin {
 			opt_each(datas, [&](PC_tree_t one_data) {
 				each(one_data, [&](PC_tree_t function_name, PC_tree_t parameters) {
 					Trigger data_trigger{to_string(function_name), parameters};
-					ctx.add_data_callback([&ctx, data_trigger](const std::string& name, Ref ref) mutable {
+					ctx.data().add_data_callback([&ctx, data_trigger](const std::string& name, Ref ref) mutable {
 						data_trigger.call(ctx);
 					}, to_string(data_name));
 				});
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 685e6923f..58ba85790 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -128,6 +128,8 @@ set(PDI_SRC
 		global_context.cxx
 		data_descriptor.cxx
 		data_descriptor_impl.cxx
+		data_store.cxx
+		data_store_impl.cxx
 		datatype.cxx
 		datatype_template.cxx
 		error.cxx
diff --git a/src/context.cxx b/src/context.cxx
index fecc2044c..2956ffc44 100644
--- a/src/context.cxx
+++ b/src/context.cxx
@@ -24,65 +24,11 @@
 
 #include "config.h"
 
-#include <iostream>
-#include <memory>
-
-#include <dlfcn.h>
-
-#include "pdi/paraconf_wrapper.h"
-#include "pdi/plugin.h"
-#include "pdi/ref_any.h"
-#include "pdi/error.h"
-
 #include "pdi/context.h"
 
 
 namespace PDI {
 
-using std::move;
-using std::string;
-using std::unordered_map;
-using std::unique_ptr;
-
-Context::Iterator::Iterator(const unordered_map<string, unique_ptr<Data_descriptor>>::iterator& data):
-	m_data(data)
-{}
-
-Context::Iterator::Iterator(unordered_map<string, unique_ptr<Data_descriptor>>::iterator&& data):
-	m_data(move(data))
-{}
-
-Data_descriptor* Context::Iterator::operator-> ()
-{
-	return m_data->second.get();
-}
-
-Data_descriptor& Context::Iterator::operator* ()
-{
-	return *m_data->second;
-}
-
-Context::Iterator& Context::Iterator::operator++ ()
-{
-	++m_data;
-	return *this;
-}
-
-bool Context::Iterator::operator!= (const Iterator& o)
-{
-	return (m_data != o.m_data);
-}
-
-Context::Iterator Context::get_iterator(const std::unordered_map<std::string, unique_ptr<Data_descriptor>>::iterator& data)
-{
-	return data;
-}
-
-Context::Iterator Context::get_iterator(std::unordered_map<std::string, unique_ptr<Data_descriptor>>::iterator&& data)
-{
-	return move(data);
-}
-
 Context::~Context() = default;
 
-}
+} //namespace PDI
diff --git a/src/context_proxy.cxx b/src/context_proxy.cxx
index da6561916..d56ee8fab 100644
--- a/src/context_proxy.cxx
+++ b/src/context_proxy.cxx
@@ -25,9 +25,10 @@
 #include <spdlog/spdlog.h>
 
 #include "pdi/context.h"
-#include "pdi/context_proxy.h"
 #include "pdi/logger.h"
 
+#include "pdi/context_proxy.h"
+
 namespace PDI {
 
 Context_proxy::Context_proxy(Context& ctx, std::string plugin_name, PC_tree_t logging_tree):
@@ -35,34 +36,9 @@ Context_proxy::Context_proxy(Context& ctx, std::string plugin_name, PC_tree_t lo
 	m_plugin_logger{configure_logger(logging_tree, plugin_name)}
 {}
 
-Data_descriptor& Context_proxy::desc(const std::string& name)
-{
-	return m_real_context.desc(name);
-}
-
-Data_descriptor& Context_proxy::desc(const char* name)
-{
-	return m_real_context.desc(name);
-}
-
-Data_descriptor& Context_proxy::operator[](const std::string& name)
-{
-	return m_real_context[name];
-}
-
-Data_descriptor& Context_proxy::operator[](const char* name)
+Data_store& Context_proxy::data()
 {
-	return m_real_context[name];
-}
-
-Context::Iterator Context_proxy::begin()
-{
-	return m_real_context.begin();
-}
-
-Context::Iterator Context_proxy::end()
-{
-	return m_real_context.end();
+	return m_real_context.data();
 }
 
 void Context_proxy::event(const char* name)
@@ -95,19 +71,9 @@ std::function<void()> Context_proxy::add_init_callback(const std::function<void(
 	return m_real_context.add_init_callback(callback);
 }
 
-std::function<void()> Context_proxy::add_data_callback(const std::function<void(const std::string&, Ref)>& callback, const std::string& name)
-{
-	return m_real_context.add_data_callback(callback, name);
-}
-
 std::function<void()> Context_proxy::add_event_callback(const std::function<void(const std::string&)>& callback, const std::string& name)
 {
 	return m_real_context.add_event_callback(callback, name);
 }
 
-std::function<void()> Context_proxy::add_empty_desc_access_callback(const std::function<void(const std::string&)>& callback, const std::string& name)
-{
-	return m_real_context.add_empty_desc_access_callback(callback, name);
-}
-
 } //namespace PDI
diff --git a/src/data_descriptor.cxx b/src/data_descriptor.cxx
index ae936f647..61bfd3d79 100644
--- a/src/data_descriptor.cxx
+++ b/src/data_descriptor.cxx
@@ -22,11 +22,23 @@
  * THE SOFTWARE.
  ******************************************************************************/
 
+#include "pdi/data_store.h"
+
 #include "pdi/data_descriptor.h"
 
 
 namespace PDI {
 
+void Data_descriptor::trigger_data_callbacks(Data_store& data_store)
+{
+	data_store.trigger_data_callbacks(name(), ref());
+}
+
+void Data_descriptor::trigger_empty_desc_access_callbacks(Data_store& data_store)
+{
+	data_store.trigger_empty_desc_access_callbacks(name());
+}
+
 Data_descriptor::~Data_descriptor() = default;
 
 } // namespace PDI
diff --git a/src/data_descriptor_impl.cxx b/src/data_descriptor_impl.cxx
index 79521c3f4..9e79289c9 100644
--- a/src/data_descriptor_impl.cxx
+++ b/src/data_descriptor_impl.cxx
@@ -27,7 +27,6 @@
 #include <functional>
 #include <iostream>
 #include <memory>
-#include <vector>
 
 #include <spdlog/spdlog.h>
 
@@ -48,7 +47,6 @@ using std::nothrow;
 using std::stack;
 using std::string;
 using std::unique_ptr;
-using std::vector;
 
 struct Data_descriptor_impl::Ref_holder {
 
@@ -78,7 +76,7 @@ struct Data_descriptor_impl::Ref_holder::Impl: Data_descriptor_impl::Ref_holder
 };
 
 
-Data_descriptor_impl::Data_descriptor_impl(Global_context& ctx, const char* name):
+Data_descriptor_impl::Data_descriptor_impl(Context& ctx, const char* name):
 	m_context{ctx},
 	m_type{UNDEF_TYPE.clone_type()},
 	m_name{name},
@@ -145,40 +143,8 @@ Ref Data_descriptor_impl::ref()
 {
 	assert((!metadata() || !m_refs.empty()) && "metadata descriptors should always keep a placeholder");
 	if (m_refs.empty()) {
-		std::vector<std::reference_wrapper<const std::function<void(const std::string&)>>> empty_desc_callbacks;
-		//add named callbacks
-		auto callback_it_pair = m_context.m_named_empty_desc_access_callbacks.equal_range(m_name);
-		for (auto it = callback_it_pair.first; it != callback_it_pair.second; it++) {
-			empty_desc_callbacks.emplace_back(std::cref(it->second));
-		}
-		//add the unnamed callbacks
-		for (auto it = m_context.m_empty_desc_access_callbacks.begin(); it != m_context.m_empty_desc_access_callbacks.end(); it++) {
-			empty_desc_callbacks.emplace_back(std::cref(*it));
-		}
-		//call gathered callbacks
-		vector<Error> errors;
-		for (const std::function<void(const std::string&)>& callback : empty_desc_callbacks) {
-			try {
-				callback(m_name);
-				//TODO: remove the faulty plugin in case of error?
-			} catch (const Error& e) {
-				errors.emplace_back(e);
-			} catch (const exception& e) {
-				errors.emplace_back(PDI_ERR_SYSTEM, e.what());
-			} catch (...) {
-				errors.emplace_back(PDI_ERR_SYSTEM, "Not std::exception based error");
-			}
-		}
-		if (!errors.empty()) {
-			if (1 == errors.size()) {
-				throw Error{errors.front().status(), "Error while triggering empty desc access `%s': %s", m_name.c_str(), errors.front().what()};
-			}
-			string errmsg = "Multiple (" + std::to_string(errors.size()) + ") errors while triggering empty desc access `" + m_name + "':\n";
-			for (auto&& err: errors) {
-				errmsg += string(err.what()) + "\n";
-			}
-			throw Error{PDI_ERR_SYSTEM, "%s", errmsg.c_str()};
-		}
+		//trigger empty desc callbacks
+		Data_descriptor::trigger_empty_desc_access_callbacks(m_context.data());
 		
 		//at least one plugin should share a Ref
 		if (m_refs.empty()) {
@@ -242,40 +208,8 @@ void* Data_descriptor_impl::share(Ref data_ref, bool read, bool write)
 		throw Error{PDI_ERR_RIGHT, "Unable to grant requested rights"};
 	}
 	
-	std::vector<std::reference_wrapper<const std::function<void(const std::string&, Ref)>>> data_callbacks;
-	//add named callbacks
-	auto callback_it_pair = m_context.m_named_data_callbacks.equal_range(m_name);
-	for (auto it = callback_it_pair.first; it != callback_it_pair.second; it++) {
-		data_callbacks.emplace_back(std::cref(it->second));
-	}
-	//add the unnamed callbacks
-	for (auto it = m_context.m_data_callbacks.begin(); it != m_context.m_data_callbacks.end(); it++) {
-		data_callbacks.emplace_back(std::cref(*it));
-	}
-	//call gathered callbacks
-	vector<Error> errors;
-	for (const std::function<void(const std::string&, Ref)>& callback : data_callbacks) {
-		try {
-			callback(m_name, ref());
-			//TODO: remove the faulty plugin in case of error?
-		} catch (const Error& e) {
-			errors.emplace_back(e);
-		} catch (const exception& e) {
-			errors.emplace_back(PDI_ERR_SYSTEM, e.what());
-		} catch (...) {
-			errors.emplace_back(PDI_ERR_SYSTEM, "Not std::exception based error");
-		}
-	}
-	if (!errors.empty()) {
-		if (1 == errors.size()) {
-			throw Error{errors.front().status(), "Error while triggering data share `%s': %s", m_name, errors.front().what()};
-		}
-		string errmsg = "Multiple (" + std::to_string(errors.size()) + ") errors while triggering data share `" + m_name + "':\n";
-		for (auto&& err: errors) {
-			errmsg += string(err.what()) + "\n";
-		}
-		throw Error{PDI_ERR_SYSTEM, "%s", errmsg.c_str()};
-	}
+	//trigger data callbacks
+	Data_descriptor::trigger_data_callbacks(m_context.data());
 	
 	assert((!metadata() || !m_refs.empty()) && "metadata descriptors should always keep a placeholder");
 	return result;
diff --git a/src/data_descriptor_impl.h b/src/data_descriptor_impl.h
index bffd9f63c..99621a7ca 100644
--- a/src/data_descriptor_impl.h
+++ b/src/data_descriptor_impl.h
@@ -36,20 +36,20 @@
 #include <pdi/datatype_template.h>
 #include <pdi/ref_any.h>
 
-#include "global_context.h"
+#include "data_store_impl.h"
 
 
 namespace PDI {
 
 class PDI_EXPORT Data_descriptor_impl : public Data_descriptor
 {
-	friend class Global_context;
+	friend class Data_store_impl;
 	friend class Descriptor_test_handler;
 	
 	struct PDI_NO_EXPORT Ref_holder;
 	
 	/// The context this descriptor is part of
-	Global_context& m_context;
+	Context& m_context;
 	
 	/// References to the values of this descriptor
 	std::stack<std::unique_ptr<Ref_holder>> m_refs;
@@ -63,7 +63,7 @@ class PDI_EXPORT Data_descriptor_impl : public Data_descriptor
 	
 	/** Create an empty descriptor
 	 */
-	Data_descriptor_impl(Global_context& ctx, const char* name);
+	Data_descriptor_impl(Context& ctx, const char* name);
 	
 	Data_descriptor_impl(const Data_descriptor_impl&) = delete;
 	
diff --git a/src/data_store.cxx b/src/data_store.cxx
new file mode 100644
index 000000000..41b8603b3
--- /dev/null
+++ b/src/data_store.cxx
@@ -0,0 +1,78 @@
+/*******************************************************************************
+ * Copyright (C) 2019 Commissariat a l'energie atomique et aux energies alternatives (CEA)
+ * Copyright (C) 2019 Institute of Bioorganic Chemistry Polish Academy of Science (PSNC)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * * Neither the name of CEA nor the names of its contributors may be used to
+ *   endorse or promote products derived from this software without specific
+ *   prior written permission.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ ******************************************************************************/
+
+#include "pdi/data_store.h"
+
+
+namespace PDI {
+
+using std::move;
+using std::string;
+using std::unordered_map;
+using std::unique_ptr;
+
+Data_store::Iterator::Iterator(const unordered_map<string, unique_ptr<Data_descriptor>>::iterator& data):
+	m_data(data)
+{}
+
+Data_store::Iterator::Iterator(unordered_map<string, unique_ptr<Data_descriptor>>::iterator&& data):
+	m_data(move(data))
+{}
+
+Data_descriptor* Data_store::Iterator::operator-> ()
+{
+	return m_data->second.get();
+}
+
+Data_descriptor& Data_store::Iterator::operator* ()
+{
+	return *m_data->second;
+}
+
+Data_store::Iterator& Data_store::Iterator::operator++ ()
+{
+	++m_data;
+	return *this;
+}
+
+bool Data_store::Iterator::operator!= (const Iterator& o)
+{
+	return (m_data != o.m_data);
+}
+
+Data_store::Iterator Data_store::get_iterator(const std::unordered_map<std::string, unique_ptr<Data_descriptor>>::iterator& data)
+{
+	return data;
+}
+
+Data_store::Iterator Data_store::get_iterator(std::unordered_map<std::string, unique_ptr<Data_descriptor>>::iterator&& data)
+{
+	return move(data);
+}
+
+Data_store::~Data_store() = default;
+
+}
+
diff --git a/src/data_store_impl.cxx b/src/data_store_impl.cxx
new file mode 100644
index 000000000..3a6989ddb
--- /dev/null
+++ b/src/data_store_impl.cxx
@@ -0,0 +1,197 @@
+/*******************************************************************************
+ * Copyright (C) 2019 Commissariat a l'energie atomique et aux energies alternatives (CEA)
+ * Copyright (C) 2019 Institute of Bioorganic Chemistry Polish Academy of Science (PSNC)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * * Neither the name of CEA nor the names of its contributors may be used to
+ *   endorse or promote products derived from this software without specific
+ *   prior written permission.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ ******************************************************************************/
+
+#include <iostream>
+#include <memory>
+#include <vector>
+
+#include "pdi/paraconf_wrapper.h"
+#include "pdi/plugin.h"
+#include "pdi/ref_any.h"
+#include "pdi/error.h"
+
+#include "data_descriptor_impl.h"
+
+#include "data_store_impl.h"
+
+
+namespace PDI {
+
+
+using std::exception;
+using std::function;
+using std::move;
+using std::reference_wrapper;
+using std::string;
+using std::to_string;
+using std::unordered_map;
+using std::unique_ptr;
+using std::vector;
+
+Data_store_impl::Data_store_impl(Context& ctx):
+	m_context{ctx}
+{}
+
+
+Data_descriptor& Data_store_impl::desc(const char* name)
+{
+	return *(m_descriptors.emplace(name, unique_ptr<Data_descriptor> {new Data_descriptor_impl{m_context, name}}).first->second);
+}
+
+Data_descriptor& Data_store_impl::desc(const string& name)
+{
+	return desc(name.c_str());
+}
+
+Data_descriptor& Data_store_impl::operator[](const char* name)
+{
+	return desc(name);
+}
+
+Data_descriptor& Data_store_impl::operator[](const string& name)
+{
+	return desc(name.c_str());
+}
+
+Data_store::Iterator Data_store_impl::begin()
+{
+	return Data_store::get_iterator(m_descriptors.begin());
+}
+
+Data_store::Iterator Data_store_impl::end()
+{
+	return Data_store::get_iterator(m_descriptors.end());
+}
+
+std::function<void()> Data_store_impl::add_data_callback(const std::function<void(const std::string&, Ref)>& callback, const std::string& name)
+{
+	if (name.empty()) {
+		m_data_callbacks.emplace_back(callback);
+		auto it = --m_data_callbacks.end();
+		return [it, this]() {
+			this->m_data_callbacks.erase(it);
+		};
+	} else {
+		auto it = m_named_data_callbacks.emplace(name, callback);
+		return [it, this]() {
+			this->m_named_data_callbacks.erase(it);
+		};
+	}
+}
+
+std::function<void()> Data_store_impl::add_empty_desc_access_callback(const std::function<void(const std::string&)>& callback, const std::string& name)
+{
+	if (name.empty()) {
+		m_empty_desc_access_callbacks.emplace_back(callback);
+		auto it = --m_empty_desc_access_callbacks.end();
+		return [it, this]() {
+			this->m_empty_desc_access_callbacks.erase(it);
+		};
+	} else {
+		auto it = m_named_empty_desc_access_callbacks.emplace(name, callback);
+		return [it, this]() {
+			this->m_named_empty_desc_access_callbacks.erase(it);
+		};
+	}
+}
+
+void Data_store_impl::trigger_data_callbacks(const string& name, Ref ref)
+{
+	vector<reference_wrapper<const function<void(const string&, Ref)>>> data_callbacks;
+	//add named callbacks
+	auto callback_it_pair = m_named_data_callbacks.equal_range(name);
+	for (auto it = callback_it_pair.first; it != callback_it_pair.second; it++) {
+		data_callbacks.emplace_back(std::cref(it->second));
+	}
+	//add the unnamed callbacks
+	for (auto it = m_data_callbacks.begin(); it != m_data_callbacks.end(); it++) {
+		data_callbacks.emplace_back(std::cref(*it));
+	}
+	//call gathered callbacks
+	vector<Error> errors;
+	for (const function<void(const string&, Ref)>& callback : data_callbacks) {
+		try {
+			callback(name, ref);
+			//TODO: remove the faulty plugin in case of error?
+		} catch (const Error& e) {
+			errors.emplace_back(e);
+		} catch (const exception& e) {
+			errors.emplace_back(PDI_ERR_SYSTEM, e.what());
+		} catch (...) {
+			errors.emplace_back(PDI_ERR_SYSTEM, "Not std::exception based error");
+		}
+	}
+	if (!errors.empty()) {
+		if (1 == errors.size()) {
+			throw Error{errors.front().status(), "Error while triggering data share `%s': %s", name, errors.front().what()};
+		}
+		string errmsg = "Multiple (" + to_string(errors.size()) + ") errors while triggering data share `" + name + "':\n";
+		for (auto&& err: errors) {
+			errmsg += string(err.what()) + "\n";
+		}
+		throw Error{PDI_ERR_SYSTEM, "%s", errmsg.c_str()};
+	}
+}
+
+void Data_store_impl::trigger_empty_desc_access_callbacks(const string& name)
+{
+	vector<reference_wrapper<const function<void(const string&)>>> empty_desc_callbacks;
+	//add named callbacks
+	auto callback_it_pair = m_named_empty_desc_access_callbacks.equal_range(name);
+	for (auto it = callback_it_pair.first; it != callback_it_pair.second; it++) {
+		empty_desc_callbacks.emplace_back(std::cref(it->second));
+	}
+	//add the unnamed callbacks
+	for (auto it = m_empty_desc_access_callbacks.begin(); it != m_empty_desc_access_callbacks.end(); it++) {
+		empty_desc_callbacks.emplace_back(std::cref(*it));
+	}
+	//call gathered callbacks
+	vector<Error> errors;
+	for (const std::function<void(const std::string&)>& callback : empty_desc_callbacks) {
+		try {
+			callback(name);
+			//TODO: remove the faulty plugin in case of error?
+		} catch (const Error& e) {
+			errors.emplace_back(e);
+		} catch (const exception& e) {
+			errors.emplace_back(PDI_ERR_SYSTEM, e.what());
+		} catch (...) {
+			errors.emplace_back(PDI_ERR_SYSTEM, "Not std::exception based error");
+		}
+	}
+	if (!errors.empty()) {
+		if (1 == errors.size()) {
+			throw Error{errors.front().status(), "Error while triggering empty desc access `%s': %s", name.c_str(), errors.front().what()};
+		}
+		string errmsg = "Multiple (" + to_string(errors.size()) + ") errors while triggering empty desc access `" + name + "':\n";
+		for (auto&& err: errors) {
+			errmsg += string(err.what()) + "\n";
+		}
+		throw Error{PDI_ERR_SYSTEM, "%s", errmsg.c_str()};
+	}
+}
+
+}
+
diff --git a/src/data_store_impl.h b/src/data_store_impl.h
new file mode 100644
index 000000000..7bbe883b7
--- /dev/null
+++ b/src/data_store_impl.h
@@ -0,0 +1,107 @@
+/*******************************************************************************
+ * Copyright (C) 2019 Commissariat a l'energie atomique et aux energies alternatives (CEA)
+ * Copyright (C) 2019 Institute of Bioorganic Chemistry Polish Academy of Science (PSNC)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * * Neither the name of CEA nor the names of its contributors may be used to
+ *   endorse or promote products derived from this software without specific
+ *   prior written permission.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ ******************************************************************************/
+
+#ifndef PDI_DATA_STORE_IMPL_H_
+#define PDI_DATA_STORE_IMPL_H_
+
+#include <functional>
+#include <list>
+#include <map>
+#include <memory>
+#include <string>
+#include <unordered_map>
+
+#include <pdi/pdi_fwd.h>
+#include <pdi/data_descriptor.h>
+#include <pdi/data_store.h>
+#include <pdi/datatype_template.h>
+#include <pdi/ref_any.h>
+
+
+namespace PDI {
+
+class PDI_EXPORT Data_store_impl: public Data_store
+{
+private:
+    Context& m_context;
+
+    /// Descriptors of the data
+    std::unordered_map<std::string, std::unique_ptr<Data_descriptor>> m_descriptors;
+	/** 
+	 *  Callbacks called when any data is available
+	 * 
+	 *  This must be a list, because valid iterators are needed to properly remove the callback by plugin
+	 */
+	std::list<std::function<void(const std::string&, Ref)>> m_data_callbacks;
+
+	/**
+	 *  Callbacks called when specified data is available.
+	 * 
+	 *  This must be an ordered multimap, because valid iterators are needed to properly remove the callback by plugin
+	 */
+	std::multimap<std::string, std::function<void(const std::string&, Ref)>> m_named_data_callbacks;
+
+    /** 
+	 *  Callbacks called on any empty desc access
+	 * 
+	 *  This must be a list, because valid iterators are needed to properly remove the callback by plugin
+	 */
+	std::list<std::function<void(const std::string&)>> m_empty_desc_access_callbacks;
+
+	/**
+	 *  Callbacks called on specified empty desc access
+	 * 
+	 *  This must be an ordered multimap, because valid iterators are needed to properly remove the callback by plugin
+	 */
+	std::multimap<std::string, std::function<void(const std::string&)>> m_named_empty_desc_access_callbacks;
+
+	void trigger_data_callbacks(const std::string& name, Ref ref) override;
+
+	void trigger_empty_desc_access_callbacks(const std::string& name) override;
+
+public:
+    Data_store_impl(Context& ctx);
+	
+	Data_descriptor& desc(const std::string& name) override;
+
+	Data_descriptor& desc(const char* name) override;
+
+	Data_descriptor& operator[](const std::string& name) override;
+
+	Data_descriptor& operator[](const char* name) override;
+
+	Iterator begin() override;
+
+	Iterator end() override;
+
+	std::function<void()> add_data_callback(const std::function<void(const std::string&, Ref)>& callback, const std::string& name = {}) override;
+	
+	std::function<void()> add_empty_desc_access_callback(const std::function<void(const std::string&)>& callback, const std::string& name = {}) override;
+
+};
+
+} // namespace PDI
+
+#endif // PDI_DATA_STORE_IMPL_H_
diff --git a/src/expression.cxx b/src/expression.cxx
index d477cdd40..2f3dab902 100644
--- a/src/expression.cxx
+++ b/src/expression.cxx
@@ -34,10 +34,12 @@
 
 #include "pdi/array_datatype.h"
 #include "pdi/context.h"
+#include "pdi/datatype.h"
+#include "pdi/data_descriptor.h"
+#include "pdi/error.h"
 #include "pdi/ref_any.h"
 #include "pdi/scalar_datatype.h"
-#include "pdi/error.h"
-#include "pdi/datatype.h"
+
 
 #include "pdi/expression.h"
 
@@ -444,7 +446,7 @@ unique_ptr<Expression::Impl> Expression::Impl::Reference_expression::clone() con
 long Expression::Impl::Reference_expression::to_long(Context& ctx) const
 try
 {
-	if (Ref_r ref = ctx.desc(m_referenced.c_str()).ref()) {
+	if (Ref_r ref = ctx.data().desc(m_referenced.c_str()).ref()) {
 		const Datatype* type = &ref.type();
 		long stride = 1;
 		long idx = 0;
@@ -505,7 +507,7 @@ Ref Expression::Impl::Reference_expression::to_ref(Context& ctx) const
 			true
 		};
 	}
-	return ctx.desc(m_referenced.c_str()).ref();
+	return ctx.data().desc(m_referenced.c_str()).ref();
 }
 
 unique_ptr<Expression::Impl> Expression::Impl::Reference_expression::parse(char const** val_str)
diff --git a/src/global_context.cxx b/src/global_context.cxx
index 59a4b11a5..7387d1b09 100644
--- a/src/global_context.cxx
+++ b/src/global_context.cxx
@@ -39,6 +39,7 @@
 #include "pdi/error.h"
 
 #include "data_descriptor_impl.h"
+#include "data_store_impl.h"
 
 #include "global_context.h"
 
@@ -67,7 +68,7 @@ void load_data(Context& ctx, PC_tree_t node, bool is_metadata)
 	int map_len = len(node);
 	
 	for (int map_id = 0; map_id < map_len; ++map_id) {
-		Data_descriptor& dsc = ctx.desc(to_string(PC_get(node, "{%d}", map_id)).c_str());
+		Data_descriptor& dsc = ctx.data().desc(to_string(PC_get(node, "{%d}", map_id)).c_str());
 		dsc.metadata(is_metadata);
 		dsc.default_type(ctx.datatype(PC_get(node, "<%d>", map_id)));
 	}
@@ -224,7 +225,8 @@ void Global_context::finalize()
 }
 
 Global_context::Global_context(PC_tree_t conf):
-	m_logger{configure_logger(PC_get(conf, ".logging"), "global")}
+	m_logger{configure_logger(PC_get(conf, ".logging"), "global")},
+	m_data{new Data_store_impl{*this}}
 {
 	// load basic datatypes
 	Datatype_template::load_basic_datatypes(*this);
@@ -265,34 +267,9 @@ Global_context::Global_context(PC_tree_t conf):
 	}
 }
 
-Data_descriptor& Global_context::desc(const char* name)
+Data_store& Global_context::data()
 {
-	return *(m_descriptors.emplace(name, unique_ptr<Data_descriptor> {new Data_descriptor_impl{*this, name}}).first->second);
-}
-
-Data_descriptor& Global_context::desc(const string& name)
-{
-	return desc(name.c_str());
-}
-
-Data_descriptor& Global_context::operator[](const char* name)
-{
-	return desc(name);
-}
-
-Data_descriptor& Global_context::operator[](const string& name)
-{
-	return desc(name.c_str());
-}
-
-Global_context::Iterator Global_context::begin()
-{
-	return Context::get_iterator(m_descriptors.begin());
-}
-
-Global_context::Iterator Global_context::end()
-{
-	return Context::get_iterator(m_descriptors.end());
+	return *m_data;
 }
 
 void Global_context::event(const char* name)
@@ -380,22 +357,6 @@ std::function<void()> Global_context::add_init_callback(const std::function<void
 	};
 }
 
-std::function<void()> Global_context::add_data_callback(const std::function<void(const std::string&, Ref)>& callback, const std::string& name)
-{
-	if (name.empty()) {
-		m_data_callbacks.emplace_back(callback);
-		auto it = --m_data_callbacks.end();
-		return [it, this]() {
-			this->m_data_callbacks.erase(it);
-		};
-	} else {
-		auto it = m_named_data_callbacks.emplace(name, callback);
-		return [it, this]() {
-			this->m_named_data_callbacks.erase(it);
-		};
-	}
-}
-
 std::function<void()> Global_context::add_event_callback(const std::function<void(const std::string&)>& callback, const std::string& name)
 {
 	if (name.empty()) {
@@ -412,21 +373,4 @@ std::function<void()> Global_context::add_event_callback(const std::function<voi
 	}
 }
 
-std::function<void()> Global_context::add_empty_desc_access_callback(const std::function<void(const std::string&)>& callback, const std::string& name)
-{
-	if (name.empty()) {
-		m_empty_desc_access_callbacks.emplace_back(callback);
-		auto it = --m_empty_desc_access_callbacks.end();
-		return [it, this]() {
-			this->m_empty_desc_access_callbacks.erase(it);
-		};
-	} else {
-		auto it = m_named_empty_desc_access_callbacks.emplace(name, callback);
-		return [it, this]() {
-			this->m_named_empty_desc_access_callbacks.erase(it);
-		};
-	}
-}
-
-
 }
diff --git a/src/global_context.h b/src/global_context.h
index a91dfd443..6e18191c9 100644
--- a/src/global_context.h
+++ b/src/global_context.h
@@ -28,7 +28,6 @@
 #include <list>
 #include <map>
 #include <memory>
-#include <stack>
 #include <string>
 #include <unordered_map>
 
@@ -45,8 +44,7 @@ namespace PDI {
 class PDI_EXPORT Global_context : public Context
 {
 private:
-	friend class Data_descriptor_impl;
-	
+
 	/// Global logger of PDI, should be constructed first, destroyed last
 	Logger_sptr m_logger;
 	
@@ -59,8 +57,8 @@ private:
 	/// Datatype_template constructors available in PDI
 	std::unordered_map<std::string, Datatype_template_parser> m_datatype_parsers;
 	
-	/// Descriptors of the data
-	std::unordered_map<std::string, std::unique_ptr<Data_descriptor>> m_descriptors;
+	/// Data store of the context
+	std::unique_ptr<Data_store> m_data;
 
 	/// The loaded plugins - need to be after m_descriptors (to guarantee proper destroy order)
 	std::unordered_map<std::string, std::unique_ptr<Plugin>> m_plugins;
@@ -72,20 +70,6 @@ private:
 	 */
 	std::list<std::function<void()>> m_init_callbacks;
 
-	/** 
-	 *  Callbacks called when any data is available
-	 * 
-	 *  This must be a list, because valid iterators are needed to properly remove the callback by plugin
-	 */
-	std::list<std::function<void(const std::string&, Ref)>> m_data_callbacks;
-
-	/**
-	 *  Callbacks called when specified data is available.
-	 * 
-	 *  This must be an ordered multimap, because valid iterators are needed to properly remove the callback by plugin
-	 */
-	std::multimap<std::string, std::function<void(const std::string&, Ref)>> m_named_data_callbacks;
-
 	/** 
 	 *  Callbacks called on any event
 	 * 
@@ -100,20 +84,6 @@ private:
 	 */
 	std::multimap<std::string, std::function<void(const std::string&)>> m_named_event_callbacks;
 
-	/** 
-	 *  Callbacks called on any empty desc access
-	 * 
-	 *  This must be a list, because valid iterators are needed to properly remove the callback by plugin
-	 */
-	std::list<std::function<void(const std::string&)>> m_empty_desc_access_callbacks;
-
-	/**
-	 *  Callbacks called on specified empty desc access
-	 * 
-	 *  This must be an ordered multimap, because valid iterators are needed to properly remove the callback by plugin
-	 */
-	std::multimap<std::string, std::function<void(const std::string&)>> m_named_empty_desc_access_callbacks;
-
 	Global_context(const Global_context&) = delete;
 	
 	Global_context(Global_context&&) = delete;
@@ -129,33 +99,8 @@ public:
 	
 	Global_context(PC_tree_t conf);
 	
-	/** Accesses the descriptor for a specific name. Might be uninitialized
-	 */
-	Data_descriptor& desc(const std::string& name) override;
-	
-	/** Accesses the descriptor for a specific name. Might be uninitialized
-	 */
-	Data_descriptor& desc(const char* name) override;
-	
-	/** Accesses the descriptor for a specific name. Might be uninitialized
-	 */
-	Data_descriptor& operator[](const std::string& name) override;
+	Data_store& data() override;
 	
-	/** Accesses the descriptor for a specific name. Might be uninitialized
-	 */
-	Data_descriptor& operator[](const char* name) override;
-	
-	/** Returns an iterator on the first descriptor
-	 */
-	Iterator begin() override;
-	
-	/** Returns an iterator past the last descriptor
-	 */
-	Iterator end() override;
-	
-	/** Triggers a PDI "event"
-	 * \param[in] name the event name
-	 */
 	void event(const char* name) override;
 	
 	Logger_sptr logger() const override;
@@ -166,11 +111,8 @@ public:
 	
 	std::function<void()> add_init_callback(const std::function<void()>& callback) override;
 	
-	std::function<void()> add_data_callback(const std::function<void(const std::string&, Ref)>& callback, const std::string& name = {}) override;
-	
 	std::function<void()> add_event_callback(const std::function<void(const std::string&)>& callback, const std::string& name = {}) override;
 	
-	std::function<void()> add_empty_desc_access_callback(const std::function<void(const std::string&)>& callback, const std::string& name = {}) override;
 };
 
 } // namespace PDI
diff --git a/src/pdi.cxx b/src/pdi.cxx
index 798194044..838569753 100644
--- a/src/pdi.cxx
+++ b/src/pdi.cxx
@@ -58,7 +58,6 @@ using std::make_shared;
 using std::move;
 using std::setfill;
 using std::setw;
-using std::stack;
 using std::string;
 using std::stringstream;
 using std::underlying_type;
@@ -279,7 +278,7 @@ PDI_status_t PDI_share(const char* name, void* buffer, PDI_inout_t access)
 try
 {
 	Paraconf_wrapper fw;
-	Global_context::context()[name].share(buffer, access & PDI_OUT, access & PDI_IN);
+	Global_context::context().data()[name].share(buffer, access & PDI_OUT, access & PDI_IN);
 	return PDI_OK;
 } catch (const Error& e)
 {
@@ -296,7 +295,7 @@ PDI_status_t PDI_access(const char* name, void** buffer, PDI_inout_t inout)
 try
 {
 	Paraconf_wrapper fw;
-	Data_descriptor& desc = Global_context::context()[name];
+	Data_descriptor& desc = Global_context::context().data()[name];
 	*buffer = desc.share(desc.ref(), inout & PDI_IN, inout & PDI_OUT);
 	return PDI_OK;
 } catch (const Error& e)
@@ -314,7 +313,7 @@ PDI_status_t PDI_release(const char* name)
 try
 {
 	Paraconf_wrapper fw;
-	Global_context::context()[name].release();
+	Global_context::context().data()[name].release();
 	return PDI_OK;
 } catch (const Error& e)
 {
@@ -331,7 +330,7 @@ PDI_status_t PDI_reclaim(const char* name)
 try
 {
 	Paraconf_wrapper fw;
-	Global_context::context()[name].reclaim();
+	Global_context::context().data()[name].reclaim();
 	return PDI_OK;
 } catch (const Error& e)
 {
diff --git a/src/python/pdi.cxx b/src/python/pdi.cxx
index ed9af83ba..1fda64533 100644
--- a/src/python/pdi.cxx
+++ b/src/python/pdi.cxx
@@ -146,7 +146,7 @@ PYBIND11_MODULE(_pdi, m)
 			static_cast<bool>(access & PDI_IN)
 		};
 		try {
-			Global_context::context()[name].share(r, false, false);
+			Global_context::context().data()[name].share(r, false, false);
 		} catch (...) {
 			// on error, do not free the data as would be done automatically otherwise
 			r.release();
@@ -156,7 +156,7 @@ PYBIND11_MODULE(_pdi, m)
 	
 	m.def("access", [](const char* name, PDI_inout_t inout) {
 		Paraconf_wrapper fw;
-		Data_descriptor& desc = Global_context::context()[name];
+		Data_descriptor& desc = Global_context::context().data()[name];
 		pyarr result = to_python(desc.ref());
 		desc.share(desc.ref(), inout & PDI_IN, inout & PDI_OUT);
 		if (!(inout & PDI_OUT)) pybind11::detail::array_descriptor_proxy(result.ptr())->flags &= ~pybind11::detail::npy_api::NPY_ARRAY_WRITEABLE_;
@@ -165,11 +165,11 @@ PYBIND11_MODULE(_pdi, m)
 	
 	m.def("release", [](const char* name) {
 		Paraconf_wrapper fw;
-		Global_context::context()[name].release();
+		Global_context::context().data()[name].release();
 	}, "Releases ownership of a data shared with PDI. PDI is then responsible to free the associated memory whenever necessary.");
 	
 	m.def("reclaim", [](const char* name) {
 		Paraconf_wrapper fw;
-		Global_context::context()[name].reclaim();
+		Global_context::context().data()[name].reclaim();
 	}, "Reclaims ownership of a data buffer shared with PDI. PDI does not manage the buffer memory anymore.");
 }
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index d1d22b453..b10dfc637 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -60,6 +60,7 @@ set(PDI_unit_tests_SRC
 		PDI_array_datatype.cxx
 		PDI_context.cxx
 		PDI_data_descriptor.cxx
+		PDI_data_store.cxx
 		PDI_error.cxx
 		PDI_expression.cxx
 		PDI_initialize_plugins.cxx
diff --git a/tests/PDI_context.cxx b/tests/PDI_context.cxx
index 58f0ff200..1d32f0d08 100644
--- a/tests/PDI_context.cxx
+++ b/tests/PDI_context.cxx
@@ -55,172 +55,6 @@ struct ContextTest : public ::testing::Test {
 	unique_ptr<Context> test_context;
 };
 
-/*
- * Name:                ContextTest.desc_string_uninitialized
- *
- * Tested functions:    PDI::Context::desc(string)
- *
- * Description:         Checks if accessesing uninitialzied descriptor
- *                      creates a new one.
- */
-TEST_F(ContextTest, desc_string_uninitialized)
-{
-	string desc_name{"test_desc"};
-	Data_descriptor& desc = this->test_context->desc(desc_name);
-	ASSERT_EQ(desc_name, desc.name());
-}
-
-/*
- * Name:                ContextTest.desc_string_initialized
- *
- * Tested functions:    PDI::Context::desc(string)
- *
- * Description:         Checks if accessesing a descriptor
- *                      returns correct one.
- */
-TEST_F(ContextTest, desc_string_initialized)
-{
-	string desc_name{"desc1"};
-	//put desc1 first to check if the same desc is returned later
-	Data_descriptor& desc1 = this->test_context->desc(desc_name);
-	
-	Data_descriptor& desc = this->test_context->desc(desc_name);
-	ASSERT_EQ(desc_name, desc.name());
-	//desc1 and desc should have the same address if they are the same desc
-	ASSERT_EQ(&desc1, &desc);
-}
-
-/*
- * Name:                ContextTest.desc_cstring_uninitialized
- *
- * Tested functions:    PDI::Context::desc(const char*)
- *
- * Description:         Checks if accessesing uninitialzied descriptor
- *                      creates a new one.
- */
-TEST_F(ContextTest, desc_cstring_uninitialized)
-{
-	const char* desc_name = "test_desc";
-	Data_descriptor& desc = this->test_context->desc(desc_name);
-	ASSERT_STREQ(desc_name, desc.name().c_str());
-}
-
-/*
- * Name:                ContextTest.desc_cstring_initialized
- *
- * Tested functions:    PDI::Context::desc(const char*)
- *
- * Description:         Checks if accessesing a descriptor
- *                      returns correct one.
- */
-TEST_F(ContextTest, desc_cstring_initialized)
-{
-	const char* desc_name = "desc1";
-	//put desc1 first to check if the same desc is returned later
-	Data_descriptor& desc1 = this->test_context->desc(desc_name);
-	
-	Data_descriptor& desc = this->test_context->desc(desc_name);
-	ASSERT_STREQ(desc_name, desc.name().c_str());
-	//desc1 and desc should have the same address if they are the same desc
-	ASSERT_EQ(&desc1, &desc);
-}
-
-/*
- * Name:                ContextTest.operator_string_uninitialized
- *
- * Tested functions:    PDI::Context::operator[](string)
- *
- * Description:         Checks if accessesing uninitialzied descriptor
- *                      creates a new one.
- */
-TEST_F(ContextTest, operator_string_uninitialized)
-{
-	string desc_name{"test_desc"};
-	Data_descriptor& desc = (*this->test_context)[desc_name];
-	ASSERT_EQ(desc_name, desc.name());
-}
-
-/*
- * Name:                ContextTest.operator_string_initialized
- *
- * Tested functions:    PDI::Context::operator[](string)
- *
- * Description:         Checks if accessesing a descriptor
- *                      returns correct one.
- */
-TEST_F(ContextTest, operator_string_initialized)
-{
-	string desc_name{"desc1"};
-	//put desc1 first to check if the same desc is returned later
-	Data_descriptor& desc1 = this->test_context->desc(desc_name);
-	
-	Data_descriptor& desc = (*this->test_context)[desc_name];
-	ASSERT_EQ(desc_name, desc.name());
-	//desc1 and desc should have the same address if they are the same desc
-	ASSERT_EQ(&desc1, &desc);
-}
-
-/*
- * Name:                ContextTest.operator_cstring_uninitialized
- *
- * Tested functions:    PDI::Context::operator[](const char*)
- *
- * Description:         Checks if accessesing uninitialzied descriptor
- *                      creates a new one.
- */
-TEST_F(ContextTest, operator_cstring_uninitialized)
-{
-	const char* desc_name = "test_desc";
-	Data_descriptor& desc = (*this->test_context)[desc_name];
-	ASSERT_STREQ(desc_name, desc.name().c_str());
-}
-
-/*
- * Name:                ContextTest.operator_cstring_initialized
- *
- * Tested functions:    PDI::Context::operator[](const char*)
- *
- * Description:         Checks if accessesing a descriptor
- *                      returns correct one.
- */
-TEST_F(ContextTest, operator_cstring_initialized)
-{
-	const char* desc_name = "desc1";
-	//put desc1 first to check if the same desc is returned later
-	Data_descriptor& desc1 = this->test_context->desc(desc_name);
-	
-	Data_descriptor& desc = (*this->test_context)[desc_name];
-	ASSERT_STREQ(desc_name, desc.name().c_str());
-	//desc1 and desc should have the same address if they are the same desc
-	ASSERT_EQ(&desc1, &desc);
-}
-
-/*
- * Name:                ContextTest.iterator
- *
- * Tested functions:    PDI::Context::begin(),
- *                      PDI::Context::end()
- *
- * Description:         Checks if tested functions
- *                      return correct iterators.
- */
-TEST_F(ContextTest, iterator)
-{
-	//put some descriptors inside context
-	set<string> desc_names{"desc1", "desc2", "desc3"};
-	for (auto& desc_name: desc_names) {
-		this->test_context->desc(desc_name);
-	}
-	auto begin = this->test_context->begin();
-	auto end = this->test_context->end();
-	
-	for (auto it = begin; it != end; ++it) {
-		auto name = desc_names.find((*it).name());
-		ASSERT_EQ(it->name(), (*it).name());
-		ASSERT_TRUE(name != desc_names.end());
-	}
-}
-
 /*
  * Name:                ContextTest.add_event
  *
@@ -319,306 +153,3 @@ TEST_F(ContextTest, add_remove_event)
 	ASSERT_EQ(x, 126);
 	ASSERT_EQ(y, 159);
 }
-
-/*
- * Name:                ContextTest.add_data_callback
- *
- * Tested functions:    PDI::Context::add_data_callback
- *
- *
- * Description:         Checks if callback is
- *                      correctly called on data share.
- *
- */
-TEST_F(ContextTest, add_data_callback)
-{
-	string data_x {"data_x"};
-	this->test_context->desc(data_x).default_type(Scalar_datatype{Scalar_kind::SIGNED, sizeof(int)}.clone_type());
-	int x = 0;
-	this->test_context->add_data_callback([](const std::string& name, Ref ref) {
-		Ref_w ref_write {ref};
-		int* x = static_cast<int*>(ref_write.get());
-		*x += 42;
-		ASSERT_STREQ(name.c_str(), "data_x");
-	});
-	ASSERT_EQ(x, 0);
-	this->test_context->desc("data_x").share(&x, true, true);
-	this->test_context->desc("data_x").reclaim();
-	ASSERT_EQ(x, 42);
-}
-
-/*
- * Name:                ContextTest.add_named_data_callback
- *
- * Tested functions:    PDI::Context::add_data_callback
- *
- *
- * Description:         Checks if named callback is
- *                      correctly called on data share.
- *
- */
-TEST_F(ContextTest, add_named_data_callback)
-{
-	string data_x {"data_x"};
-	string data_y {"data_y"};
-	this->test_context->desc(data_x).default_type(Scalar_datatype{Scalar_kind::SIGNED, sizeof(int)}.clone_type());
-	this->test_context->desc(data_y).default_type(Scalar_datatype{Scalar_kind::SIGNED, sizeof(int)}.clone_type());
-	int x = 0;
-	int y = 0;
-	this->test_context->add_data_callback([](const std::string& name, Ref ref) {
-		Ref_w ref_write {ref};
-		int* x = static_cast<int*>(ref_write.get());
-		*x += 42;
-		ASSERT_STREQ(name.c_str(), "data_x");
-	}, "data_x");
-	ASSERT_EQ(x, 0);
-	ASSERT_EQ(y, 0);
-	this->test_context->desc("data_x").share(&x, true, true);
-	this->test_context->desc("data_x").reclaim();
-	ASSERT_EQ(x, 42);
-	ASSERT_EQ(y, 0);
-}
-
-/*
- * Name:                ContextTest.remove_data_callback
- *
- * Tested functions:    PDI::Context::add_data_callback
- *
- *
- * Description:         Checks if callback is
- *                      correctly called on share
- *                      and removes it.
- */
-TEST_F(ContextTest, remove_data_callback)
-{
-	string data_x {"data_x"};
-	this->test_context->desc(data_x).default_type(Scalar_datatype{Scalar_kind::SIGNED, sizeof(int)}.clone_type());
-	int x = 0;
-	auto erase_x = this->test_context->add_data_callback([](const std::string& name, Ref ref) {
-		Ref_w ref_write {ref};
-		int* x = static_cast<int*>(ref_write.get());
-		*x += 42;
-		ASSERT_STREQ(name.c_str(), "data_x");
-	});
-	ASSERT_EQ(x, 0);
-	this->test_context->desc("data_x").share(&x, true, true);
-	this->test_context->desc("data_x").reclaim();
-	ASSERT_EQ(x, 42);
-	erase_x();
-	this->test_context->desc("data_x").share(&x, true, true);
-	this->test_context->desc("data_x").reclaim();
-	ASSERT_EQ(x, 42);
-}
-
-/*
- * Name:                ContextTest.remove_named_data_callback
- *
- * Tested functions:    PDI::Context::add_data_callback
- *
- *
- * Description:         Checks if named callback is
- *                      correctly called on data share.
- *
- */
-TEST_F(ContextTest, remove_named_data_callback)
-{
-	string data_x {"data_x"};
-	string data_y {"data_y"};
-	this->test_context->desc(data_x).default_type(Scalar_datatype{Scalar_kind::SIGNED, sizeof(int)}.clone_type());
-	this->test_context->desc(data_y).default_type(Scalar_datatype{Scalar_kind::SIGNED, sizeof(int)}.clone_type());
-	int x = 0;
-	int y = 0;
-	auto erase_x = this->test_context->add_data_callback([](const std::string& name, Ref ref) {
-		Ref_w ref_write {ref};
-		int* x = static_cast<int*>(ref_write.get());
-		*x += 42;
-		ASSERT_STREQ(name.c_str(), "data_x");
-	}, "data_x");
-	ASSERT_EQ(x, 0);
-	ASSERT_EQ(y, 0);
-	this->test_context->desc("data_x").share(&x, true, true);
-	this->test_context->desc("data_x").reclaim();
-	ASSERT_EQ(x, 42);
-	ASSERT_EQ(y, 0);
-	erase_x();
-	this->test_context->desc("data_x").share(&x, true, true);
-	this->test_context->desc("data_x").reclaim();
-	ASSERT_EQ(x, 42);
-	ASSERT_EQ(y, 0);
-}
-
-/*
- * Name:                ContextTest.add_remove_data_callback
- *
- * Tested functions:    PDI::Context::add_data_callback
- *
- *
- * Description:         Checks if callback is
- *                      correctly called on share
- *                      and removes it several times.
- *
- */
-TEST_F(ContextTest, add_remove_data_callback)
-{
-	string data_x {"data_x"};
-	string data_y {"data_y"};
-	Data_descriptor& desc_x = this->test_context->desc(data_x);
-	Data_descriptor& desc_y = this->test_context->desc(data_y);
-	this->test_context->desc(data_x).default_type(Scalar_datatype{Scalar_kind::SIGNED, sizeof(int)}.clone_type());
-	this->test_context->desc(data_y).default_type(Scalar_datatype{Scalar_kind::SIGNED, sizeof(int)}.clone_type());
-	int x = 0;
-	int y = 0;
-	auto erase_x = this->test_context->add_data_callback([](const std::string& name, Ref ref) {
-		Ref_w ref_write {ref};
-		int* x = static_cast<int*>(ref_write.get());
-		*x += std::stoi(name);
-	});
-	auto erase_y = this->test_context->add_data_callback([](const std::string& name, Ref ref) {
-		Ref_w ref_write {ref};
-		int* y = static_cast<int*>(ref_write.get());
-		*y += std::stoi(name) + 1;
-	});
-	ASSERT_EQ(x, 0);
-	ASSERT_EQ(y, 0);
-	this->test_context->desc("1").share(&x, true, true);
-	this->test_context->desc("1").reclaim();
-	ASSERT_EQ(x, 3);
-	ASSERT_EQ(y, 0);
-	this->test_context->desc("2").share(&y, true, true);
-	this->test_context->desc("2").reclaim();
-	ASSERT_EQ(x, 3);
-	ASSERT_EQ(y, 5);
-	erase_x();
-	this->test_context->desc("3").share(&x, true, true);
-	this->test_context->desc("3").reclaim();
-	ASSERT_EQ(x, 7);
-	ASSERT_EQ(y, 5);
-	this->test_context->desc("4").share(&y, true, true);
-	this->test_context->desc("4").reclaim();
-	ASSERT_EQ(x, 7);
-	ASSERT_EQ(y, 10);
-	erase_y();
-	this->test_context->desc("5").share(&x, true, true);
-	this->test_context->desc("6").share(&y, true, true);
-	this->test_context->desc("5").reclaim();
-	this->test_context->desc("6").reclaim();
-	ASSERT_EQ(x, 7);
-	ASSERT_EQ(y, 10);
-}
-
-/*
- * Name:                ContextTest.add_remove_named_data_callback
- *
- * Tested functions:    PDI::Context::add_data_callback
- *
- *
- * Description:         Checks if named callback is
- *                      correctly called on share
- *                      and removes it several times.
- *
- */
-TEST_F(ContextTest, add_remove_named_data_callback)
-{
-	string data_x {"data_x"};
-	string data_y {"data_y"};
-	Data_descriptor& desc_x = this->test_context->desc(data_x);
-	Data_descriptor& desc_y = this->test_context->desc(data_y);
-	this->test_context->desc(data_x).default_type(Scalar_datatype{Scalar_kind::SIGNED, sizeof(int)}.clone_type());
-	this->test_context->desc(data_y).default_type(Scalar_datatype{Scalar_kind::SIGNED, sizeof(int)}.clone_type());
-	int x = 0;
-	int y = 0;
-	auto erase_x = this->test_context->add_data_callback([](const std::string& name, Ref ref) {
-		Ref_w ref_write {ref};
-		int* x = static_cast<int*>(ref_write.get());
-		*x += 42;
-		ASSERT_STREQ(name.c_str(), "data_x");
-	}, "data_x");
-	auto erase_y = this->test_context->add_data_callback([](const std::string& name, Ref ref) {
-		Ref_w ref_write {ref};
-		int* y = static_cast<int*>(ref_write.get());
-		*y += 53;
-		ASSERT_STREQ(name.c_str(), "data_y");
-	}, "data_y");
-	ASSERT_EQ(x, 0);
-	ASSERT_EQ(y, 0);
-	this->test_context->desc("data_x").share(&x, true, true);
-	this->test_context->desc("data_x").reclaim();
-	ASSERT_EQ(x, 42);
-	ASSERT_EQ(y, 0);
-	this->test_context->desc("data_y").share(&y, true, true);
-	this->test_context->desc("data_y").reclaim();
-	ASSERT_EQ(x, 42);
-	ASSERT_EQ(y, 53);
-	erase_x();
-	this->test_context->desc("data_x").share(&x, true, true);
-	this->test_context->desc("data_x").reclaim();
-	ASSERT_EQ(x, 42);
-	ASSERT_EQ(y, 53);
-	this->test_context->desc("data_y").share(&y, true, true);
-	this->test_context->desc("data_y").reclaim();
-	ASSERT_EQ(x, 42);
-	ASSERT_EQ(y, 106);
-	erase_y();
-	this->test_context->desc("data_y").share(&y, true, true);
-	this->test_context->desc("data_y").reclaim();
-	ASSERT_EQ(x, 42);
-	ASSERT_EQ(y, 106);
-}
-
-/*
- * Name:                ContextTest.add_empty_desc_callback
- *
- * Tested functions:    PDI::Context::add_empty_desc_access_callback
- *
- *
- * Description:         Checks if callback is
- *                      correctly called on empty desc access.
- */
-TEST_F(ContextTest, add_empty_desc_callback)
-{
-	string data_x {"data_x"};
-	this->test_context->desc(data_x).default_type(Scalar_datatype{Scalar_kind::SIGNED, sizeof(int)}.clone_type());
-	this->test_context->add_empty_desc_access_callback([this](const std::string& name) {
-		int* x = new int;
-		*x = 42;
-		this->test_context->desc(name).share(x, true, true);
-	});
-	Ref_r ref_read {this->test_context->desc(data_x).ref()};
-	int x = *static_cast<const int*>(ref_read.get());
-	ASSERT_EQ(x, 42);
-	int* data = static_cast<int*>(this->test_context->desc(data_x).reclaim());
-	delete data;
-}
-
-/*
- * Name:                ContextTest.remove_empty_desc_callback
- *
- * Tested functions:    PDI::Context::add_empty_desc_access_callback
- *
- *
- * Description:         Checks if callback is
- *                      correctly called on empty desc access
- *                      and removes it.
- */
-TEST_F(ContextTest, remove_empty_desc_callback)
-{
-	string data_x {"data_x"};
-	this->test_context->desc(data_x).default_type(Scalar_datatype{Scalar_kind::SIGNED, sizeof(int)}.clone_type());
-	auto erase_x = this->test_context->add_empty_desc_access_callback([this](const std::string& name) {
-		int* x = new int;
-		*x = 42;
-		this->test_context->desc(name).share(x, true, true);
-	});
-	Ref_r ref_read {this->test_context->desc(data_x).ref()};
-	int x = *static_cast<const int*>(ref_read.get());
-	ASSERT_EQ(x, 42);
-	int* data = static_cast<int*>(this->test_context->desc(data_x).reclaim());
-	delete data;
-	erase_x();
-	try {
-		Ref ref_x {this->test_context->desc(data_x).ref()};
-		FAIL();
-	} catch (Error e) {
-		ASSERT_EQ(e.status(), PDI_ERR_VALUE);
-	}
-}
diff --git a/tests/PDI_data_store.cxx b/tests/PDI_data_store.cxx
new file mode 100644
index 000000000..13ab681bd
--- /dev/null
+++ b/tests/PDI_data_store.cxx
@@ -0,0 +1,534 @@
+/*******************************************************************************
+ * Copyright (C) 2019 Institute of Bioorganic Chemistry Polish Academy of Science (PSNC)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * * Neither the name of CEA nor the names of its contributors may be used to
+ *   endorse or promote products derived from this software without specific
+ *   prior written permission.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ ******************************************************************************/
+
+#include <gtest/gtest.h>
+
+#include <pdi/data_store.h>
+#include <pdi/paraconf_wrapper.h>
+#include <pdi/plugin.h>
+#include <pdi/scalar_datatype.h>
+
+#include "mocks/context_mock.h"
+#include "data_store_impl.h"
+
+
+using namespace PDI;
+using std::string;
+using std::unique_ptr;
+using std::set;
+
+/*
+ * Struct prepared for DataStoreTest.
+ */
+struct DataStoreTest : public ::testing::Test {
+
+	void SetUp() override
+	{
+		test_data_store.reset(new Data_store_impl{context_mock});
+	}
+	
+	Paraconf_wrapper fw;
+	MockContext context_mock;
+	unique_ptr<Data_store> test_data_store;
+};
+
+/*
+ * Name:                DataStoreTest.desc_string_uninitialized
+ *
+ * Tested functions:    PDI::Data_store::desc(string)
+ *
+ * Description:         Checks if accessesing uninitialzied descriptor
+ *                      creates a new one.
+ */
+TEST_F(DataStoreTest, desc_string_uninitialized)
+{
+	string desc_name{"test_desc"};
+	Data_descriptor& desc = this->test_data_store->desc(desc_name);
+	ASSERT_EQ(desc_name, desc.name());
+}
+
+/*
+ * Name:                DataStoreTest.desc_string_initialized
+ *
+ * Tested functions:    PDI::Data_store::desc(string)
+ *
+ * Description:         Checks if accessesing a descriptor
+ *                      returns correct one.
+ */
+TEST_F(DataStoreTest, desc_string_initialized)
+{
+	string desc_name{"desc1"};
+	//put desc1 first to check if the same desc is returned later
+	Data_descriptor& desc1 = this->test_data_store->desc(desc_name);
+	
+	Data_descriptor& desc = this->test_data_store->desc(desc_name);
+	ASSERT_EQ(desc_name, desc.name());
+	//desc1 and desc should have the same address if they are the same desc
+	ASSERT_EQ(&desc1, &desc);
+}
+
+/*
+ * Name:                DataStoreTest.desc_cstring_uninitialized
+ *
+ * Tested functions:    PDI::Data_store::desc(const char*)
+ *
+ * Description:         Checks if accessesing uninitialzied descriptor
+ *                      creates a new one.
+ */
+TEST_F(DataStoreTest, desc_cstring_uninitialized)
+{
+	const char* desc_name = "test_desc";
+	Data_descriptor& desc = this->test_data_store->desc(desc_name);
+	ASSERT_STREQ(desc_name, desc.name().c_str());
+}
+
+/*
+ * Name:                DataStoreTest.desc_cstring_initialized
+ *
+ * Tested functions:    PDI::Data_store::desc(const char*)
+ *
+ * Description:         Checks if accessesing a descriptor
+ *                      returns correct one.
+ */
+TEST_F(DataStoreTest, desc_cstring_initialized)
+{
+	const char* desc_name = "desc1";
+	//put desc1 first to check if the same desc is returned later
+	Data_descriptor& desc1 = this->test_data_store->desc(desc_name);
+	
+	Data_descriptor& desc = this->test_data_store->desc(desc_name);
+	ASSERT_STREQ(desc_name, desc.name().c_str());
+	//desc1 and desc should have the same address if they are the same desc
+	ASSERT_EQ(&desc1, &desc);
+}
+
+/*
+ * Name:                DataStoreTest.operator_string_uninitialized
+ *
+ * Tested functions:    PDI::Data_store::operator[](string)
+ *
+ * Description:         Checks if accessesing uninitialzied descriptor
+ *                      creates a new one.
+ */
+TEST_F(DataStoreTest, operator_string_uninitialized)
+{
+	string desc_name{"test_desc"};
+	Data_descriptor& desc = (*this->test_data_store)[desc_name];
+	ASSERT_EQ(desc_name, desc.name());
+}
+
+/*
+ * Name:                DataStoreTest.operator_string_initialized
+ *
+ * Tested functions:    PDI::Data_store::operator[](string)
+ *
+ * Description:         Checks if accessesing a descriptor
+ *                      returns correct one.
+ */
+TEST_F(DataStoreTest, operator_string_initialized)
+{
+	string desc_name{"desc1"};
+	//put desc1 first to check if the same desc is returned later
+	Data_descriptor& desc1 = this->test_data_store->desc(desc_name);
+	
+	Data_descriptor& desc = (*this->test_data_store)[desc_name];
+	ASSERT_EQ(desc_name, desc.name());
+	//desc1 and desc should have the same address if they are the same desc
+	ASSERT_EQ(&desc1, &desc);
+}
+
+/*
+ * Name:                DataStoreTest.operator_cstring_uninitialized
+ *
+ * Tested functions:    PDI::Data_store::operator[](const char*)
+ *
+ * Description:         Checks if accessesing uninitialzied descriptor
+ *                      creates a new one.
+ */
+TEST_F(DataStoreTest, operator_cstring_uninitialized)
+{
+	const char* desc_name = "test_desc";
+	Data_descriptor& desc = (*this->test_data_store)[desc_name];
+	ASSERT_STREQ(desc_name, desc.name().c_str());
+}
+
+/*
+ * Name:                DataStoreTest.operator_cstring_initialized
+ *
+ * Tested functions:    PDI::Data_store::operator[](const char*)
+ *
+ * Description:         Checks if accessesing a descriptor
+ *                      returns correct one.
+ */
+TEST_F(DataStoreTest, operator_cstring_initialized)
+{
+	const char* desc_name = "desc1";
+	//put desc1 first to check if the same desc is returned later
+	Data_descriptor& desc1 = this->test_data_store->desc(desc_name);
+	
+	Data_descriptor& desc = (*this->test_data_store)[desc_name];
+	ASSERT_STREQ(desc_name, desc.name().c_str());
+	//desc1 and desc should have the same address if they are the same desc
+	ASSERT_EQ(&desc1, &desc);
+}
+
+/*
+ * Name:                DataStoreTest.iterator
+ *
+ * Tested functions:    PDI::Data_store::begin(),
+ *                      PDI::Data_store::end()
+ *
+ * Description:         Checks if tested functions
+ *                      return correct iterators.
+ */
+TEST_F(DataStoreTest, iterator)
+{
+	//put some descriptors inside Data_store
+	set<string> desc_names{"desc1", "desc2", "desc3"};
+	
+	for (auto& desc_name: desc_names) {
+		this->test_data_store->desc(desc_name);
+	}
+	auto begin = this->test_data_store->begin();
+	auto end = this->test_data_store->end();
+	
+	for (auto it = begin; it != end; ++it) {
+		auto name = desc_names.find((*it).name());
+		ASSERT_EQ(it->name(), (*it).name());
+		ASSERT_TRUE(name != desc_names.end());
+	}
+}
+
+/*
+ * Name:                DataStoreTest.add_data_callback
+ *
+ * Tested functions:    PDI::Data_store::add_data_callback
+ *
+ *
+ * Description:         Checks if callback is
+ *                      correctly called on data share.
+ *
+ */
+TEST_F(DataStoreTest, add_data_callback)
+{
+	string data_x {"data_x"};
+	EXPECT_CALL(context_mock, data()).WillOnce(testing::ReturnRef(*test_data_store));
+	this->test_data_store->desc(data_x).default_type(Scalar_datatype{Scalar_kind::SIGNED, sizeof(int)}.clone_type());
+	int x = 0;
+	this->test_data_store->add_data_callback([](const std::string& name, Ref ref) {
+		Ref_w ref_write {ref};
+		int* x = static_cast<int*>(ref_write.get());
+		*x += 42;
+		ASSERT_STREQ(name.c_str(), "data_x");
+	});
+	ASSERT_EQ(x, 0);
+	this->test_data_store->desc("data_x").share(&x, true, true);
+	this->test_data_store->desc("data_x").reclaim();
+	ASSERT_EQ(x, 42);
+}
+
+/*
+ * Name:                DataStoreTest.add_named_data_callback
+ *
+ * Tested functions:    PDI::Data_store::add_data_callback
+ *
+ *
+ * Description:         Checks if named callback is
+ *                      correctly called on data share.
+ *
+ */
+TEST_F(DataStoreTest, add_named_data_callback)
+{
+	string data_x {"data_x"};
+	string data_y {"data_y"};
+	EXPECT_CALL(context_mock, data()).WillOnce(testing::ReturnRef(*test_data_store));
+	this->test_data_store->desc(data_x).default_type(Scalar_datatype{Scalar_kind::SIGNED, sizeof(int)}.clone_type());
+	this->test_data_store->desc(data_y).default_type(Scalar_datatype{Scalar_kind::SIGNED, sizeof(int)}.clone_type());
+	int x = 0;
+	int y = 0;
+	this->test_data_store->add_data_callback([](const std::string& name, Ref ref) {
+		Ref_w ref_write {ref};
+		int* x = static_cast<int*>(ref_write.get());
+		*x += 42;
+		ASSERT_STREQ(name.c_str(), "data_x");
+	}, "data_x");
+	ASSERT_EQ(x, 0);
+	ASSERT_EQ(y, 0);
+	this->test_data_store->desc("data_x").share(&x, true, true);
+	this->test_data_store->desc("data_x").reclaim();
+	ASSERT_EQ(x, 42);
+	ASSERT_EQ(y, 0);
+}
+
+/*
+ * Name:                DataStoreTest.remove_data_callback
+ *
+ * Tested functions:    PDI::Data_store::add_data_callback
+ *
+ *
+ * Description:         Checks if callback is
+ *                      correctly called on share
+ *                      and removes it.
+ */
+TEST_F(DataStoreTest, remove_data_callback)
+{
+	string data_x {"data_x"};
+	EXPECT_CALL(context_mock, data()).Times(2).WillRepeatedly(testing::ReturnRef(*test_data_store));
+	this->test_data_store->desc(data_x).default_type(Scalar_datatype{Scalar_kind::SIGNED, sizeof(int)}.clone_type());
+	int x = 0;
+	auto erase_x = this->test_data_store->add_data_callback([](const std::string& name, Ref ref) {
+		Ref_w ref_write {ref};
+		int* x = static_cast<int*>(ref_write.get());
+		*x += 42;
+		ASSERT_STREQ(name.c_str(), "data_x");
+	});
+	ASSERT_EQ(x, 0);
+	this->test_data_store->desc("data_x").share(&x, true, true);
+	this->test_data_store->desc("data_x").reclaim();
+	ASSERT_EQ(x, 42);
+	erase_x();
+	this->test_data_store->desc("data_x").share(&x, true, true);
+	this->test_data_store->desc("data_x").reclaim();
+	ASSERT_EQ(x, 42);
+}
+
+/*
+ * Name:                DataStoreTest.remove_named_data_callback
+ *
+ * Tested functions:    PDI::Data_store::add_data_callback
+ *
+ *
+ * Description:         Checks if named callback is
+ *                      correctly called on data share.
+ *
+ */
+TEST_F(DataStoreTest, remove_named_data_callback)
+{
+	string data_x {"data_x"};
+	string data_y {"data_y"};
+	EXPECT_CALL(context_mock, data()).Times(2).WillRepeatedly(testing::ReturnRef(*test_data_store));
+	this->test_data_store->desc(data_x).default_type(Scalar_datatype{Scalar_kind::SIGNED, sizeof(int)}.clone_type());
+	this->test_data_store->desc(data_y).default_type(Scalar_datatype{Scalar_kind::SIGNED, sizeof(int)}.clone_type());
+	int x = 0;
+	int y = 0;
+	auto erase_x = this->test_data_store->add_data_callback([](const std::string& name, Ref ref) {
+		Ref_w ref_write {ref};
+		int* x = static_cast<int*>(ref_write.get());
+		*x += 42;
+		ASSERT_STREQ(name.c_str(), "data_x");
+	}, "data_x");
+	ASSERT_EQ(x, 0);
+	ASSERT_EQ(y, 0);
+	this->test_data_store->desc("data_x").share(&x, true, true);
+	this->test_data_store->desc("data_x").reclaim();
+	ASSERT_EQ(x, 42);
+	ASSERT_EQ(y, 0);
+	erase_x();
+	this->test_data_store->desc("data_x").share(&x, true, true);
+	this->test_data_store->desc("data_x").reclaim();
+	ASSERT_EQ(x, 42);
+	ASSERT_EQ(y, 0);
+}
+
+/*
+ * Name:                DataStoreTest.add_remove_data_callback
+ *
+ * Tested functions:    PDI::Data_store::add_data_callback
+ *
+ *
+ * Description:         Checks if callback is
+ *                      correctly called on share
+ *                      and removes it several times.
+ *
+ */
+TEST_F(DataStoreTest, add_remove_data_callback)
+{
+	string data_x {"data_x"};
+	string data_y {"data_y"};
+	Data_descriptor& desc_x = this->test_data_store->desc(data_x);
+	Data_descriptor& desc_y = this->test_data_store->desc(data_y);
+	this->test_data_store->desc(data_x).default_type(Scalar_datatype{Scalar_kind::SIGNED, sizeof(int)}.clone_type());
+	this->test_data_store->desc(data_y).default_type(Scalar_datatype{Scalar_kind::SIGNED, sizeof(int)}.clone_type());
+	int x = 0;
+	int y = 0;
+	auto erase_x = this->test_data_store->add_data_callback([](const std::string& name, Ref ref) {
+		Ref_w ref_write {ref};
+		int* x = static_cast<int*>(ref_write.get());
+		*x += std::stoi(name);
+	});
+	auto erase_y = this->test_data_store->add_data_callback([](const std::string& name, Ref ref) {
+		Ref_w ref_write {ref};
+		int* y = static_cast<int*>(ref_write.get());
+		*y += std::stoi(name) + 1;
+	});
+	ASSERT_EQ(x, 0);
+	ASSERT_EQ(y, 0);
+	EXPECT_CALL(context_mock, data())
+	.Times(6).WillRepeatedly(testing::ReturnRef(*test_data_store));
+	this->test_data_store->desc("1").share(&x, true, true);
+	this->test_data_store->desc("1").reclaim();
+	ASSERT_EQ(x, 3);
+	ASSERT_EQ(y, 0);
+	this->test_data_store->desc("2").share(&y, true, true);
+	this->test_data_store->desc("2").reclaim();
+	ASSERT_EQ(x, 3);
+	ASSERT_EQ(y, 5);
+	erase_x();
+	this->test_data_store->desc("3").share(&x, true, true);
+	this->test_data_store->desc("3").reclaim();
+	ASSERT_EQ(x, 7);
+	ASSERT_EQ(y, 5);
+	this->test_data_store->desc("4").share(&y, true, true);
+	this->test_data_store->desc("4").reclaim();
+	ASSERT_EQ(x, 7);
+	ASSERT_EQ(y, 10);
+	erase_y();
+	this->test_data_store->desc("5").share(&x, true, true);
+	this->test_data_store->desc("6").share(&y, true, true);
+	this->test_data_store->desc("5").reclaim();
+	this->test_data_store->desc("6").reclaim();
+	ASSERT_EQ(x, 7);
+	ASSERT_EQ(y, 10);
+}
+
+/*
+ * Name:                DataStoreTest.add_remove_named_data_callback
+ *
+ * Tested functions:    PDI::Data_store::add_data_callback
+ *
+ *
+ * Description:         Checks if named callback is
+ *                      correctly called on share
+ *                      and removes it several times.
+ *
+ */
+TEST_F(DataStoreTest, add_remove_named_data_callback)
+{
+	string data_x {"data_x"};
+	string data_y {"data_y"};
+	Data_descriptor& desc_x = this->test_data_store->desc(data_x);
+	Data_descriptor& desc_y = this->test_data_store->desc(data_y);
+	this->test_data_store->desc(data_x).default_type(Scalar_datatype{Scalar_kind::SIGNED, sizeof(int)}.clone_type());
+	this->test_data_store->desc(data_y).default_type(Scalar_datatype{Scalar_kind::SIGNED, sizeof(int)}.clone_type());
+	int x = 0;
+	int y = 0;
+	auto erase_x = this->test_data_store->add_data_callback([](const std::string& name, Ref ref) {
+		Ref_w ref_write {ref};
+		int* x = static_cast<int*>(ref_write.get());
+		*x += 42;
+		ASSERT_STREQ(name.c_str(), "data_x");
+	}, "data_x");
+	auto erase_y = this->test_data_store->add_data_callback([](const std::string& name, Ref ref) {
+		Ref_w ref_write {ref};
+		int* y = static_cast<int*>(ref_write.get());
+		*y += 53;
+		ASSERT_STREQ(name.c_str(), "data_y");
+	}, "data_y");
+	ASSERT_EQ(x, 0);
+	ASSERT_EQ(y, 0);
+	EXPECT_CALL(context_mock, data())
+	.Times(5).WillRepeatedly(testing::ReturnRef(*test_data_store));
+	this->test_data_store->desc("data_x").share(&x, true, true);
+	this->test_data_store->desc("data_x").reclaim();
+	ASSERT_EQ(x, 42);
+	ASSERT_EQ(y, 0);
+	this->test_data_store->desc("data_y").share(&y, true, true);
+	this->test_data_store->desc("data_y").reclaim();
+	ASSERT_EQ(x, 42);
+	ASSERT_EQ(y, 53);
+	erase_x();
+	this->test_data_store->desc("data_x").share(&x, true, true);
+	this->test_data_store->desc("data_x").reclaim();
+	ASSERT_EQ(x, 42);
+	ASSERT_EQ(y, 53);
+	this->test_data_store->desc("data_y").share(&y, true, true);
+	this->test_data_store->desc("data_y").reclaim();
+	ASSERT_EQ(x, 42);
+	ASSERT_EQ(y, 106);
+	erase_y();
+	this->test_data_store->desc("data_y").share(&y, true, true);
+	this->test_data_store->desc("data_y").reclaim();
+	ASSERT_EQ(x, 42);
+	ASSERT_EQ(y, 106);
+}
+
+/*
+ * Name:                DataStoreTest.add_empty_desc_callback
+ *
+ * Tested functions:    PDI::Data_store::add_empty_desc_access_callback
+ *
+ *
+ * Description:         Checks if callback is
+ *                      correctly called on empty desc access.
+ */
+TEST_F(DataStoreTest, add_empty_desc_callback)
+{
+	string data_x {"data_x"};
+	EXPECT_CALL(context_mock, data()).WillRepeatedly(testing::ReturnRef(*test_data_store));
+	this->test_data_store->desc(data_x).default_type(Scalar_datatype{Scalar_kind::SIGNED, sizeof(int)}.clone_type());
+	this->test_data_store->add_empty_desc_access_callback([this](const std::string& name) {
+		int* x = new int;
+		*x = 42;
+		this->test_data_store->desc(name).share(x, true, true);
+	});
+	Ref_r ref_read {this->test_data_store->desc(data_x).ref()};
+	int x = *static_cast<const int*>(ref_read.get());
+	ASSERT_EQ(x, 42);
+	int* data = static_cast<int*>(this->test_data_store->desc(data_x).reclaim());
+	delete data;
+}
+
+/*
+ * Name:                DataStoreTest.remove_empty_desc_callback
+ *
+ * Tested functions:    PDI::Data_store::add_empty_desc_access_callback
+ *
+ *
+ * Description:         Checks if callback is
+ *                      correctly called on empty desc access
+ *                      and removes it.
+ */
+TEST_F(DataStoreTest, remove_empty_desc_callback)
+{
+	string data_x {"data_x"};
+	EXPECT_CALL(context_mock, data()).WillRepeatedly(testing::ReturnRef(*test_data_store));
+	this->test_data_store->desc(data_x).default_type(Scalar_datatype{Scalar_kind::SIGNED, sizeof(int)}.clone_type());
+	auto erase_x = this->test_data_store->add_empty_desc_access_callback([this](const std::string& name) {
+		int* x = new int;
+		*x = 42;
+		this->test_data_store->desc(name).share(x, true, true);
+	});
+	Ref_r ref_read {this->test_data_store->desc(data_x).ref()};
+	int x = *static_cast<const int*>(ref_read.get());
+	ASSERT_EQ(x, 42);
+	int* data = static_cast<int*>(this->test_data_store->desc(data_x).reclaim());
+	delete data;
+	erase_x();
+	try {
+		Ref ref_x {this->test_data_store->desc(data_x).ref()};
+		FAIL();
+	} catch (Error e) {
+		ASSERT_EQ(e.status(), PDI_ERR_VALUE);
+	}
+}
diff --git a/tests/PDI_expression.cxx b/tests/PDI_expression.cxx
index 810b7e9de..bbe50b6fd 100644
--- a/tests/PDI_expression.cxx
+++ b/tests/PDI_expression.cxx
@@ -35,6 +35,7 @@
 
 #include "mocks/context_mock.h"
 #include "mocks/data_descriptor_mock.h"
+#include "mocks/data_store_mock.h"
 
 using PDI::Expression;
 using PDI::Datatype_uptr;
@@ -638,9 +639,11 @@ TEST_F(AdvancedExpressionTest, simple_reference)
 {
 
 	MockDataDescriptor desc_mock;
+	MockDataStore data_store_mock;
 	long value = 10l;
 	EXPECT_CALL(desc_mock, ref()).WillOnce(Return(Ref_r{new long{value}, &free, Datatype_uptr{new Scalar_datatype{Scalar_kind::SIGNED, sizeof(long)}},true, true}));
-	EXPECT_CALL(context_mock, desc(Matcher<const char*>(StrEq("simple")))).WillOnce(ReturnRef(desc_mock));
+	EXPECT_CALL(data_store_mock, desc(Matcher<const char*>(StrEq("simple")))).WillOnce(ReturnRef(desc_mock));
+	EXPECT_CALL(context_mock, data()).WillOnce(ReturnRef(data_store_mock));
 	Expression exp{"$simple"};
 	ASSERT_EQ(value, exp.to_long(context_mock));
 }
@@ -678,12 +681,14 @@ TEST_F(AdvancedExpressionTest, invalid_identifiers)
 TEST_F(AdvancedExpressionTest, reference_in_operation)
 {
 	MockDataDescriptor desc_mock;
+	MockDataStore data_store_mock;
 	long value1 = 10l;
 	long value2 = 20l;
 	EXPECT_CALL(desc_mock, ref()).WillOnce(Return(Ref_r{new long{value1}, &free, Datatype_uptr{new Scalar_datatype{Scalar_kind::SIGNED, sizeof(long)}},true, true}))
 	.WillOnce(Return(Ref_r{new long{value2}, &free, Datatype_uptr{new Scalar_datatype{Scalar_kind::SIGNED, sizeof(long)}},true, true}));
-	EXPECT_CALL(context_mock, desc(Matcher<const char*>(StrEq("value1")))).WillOnce(ReturnRef(desc_mock));
-	EXPECT_CALL(context_mock, desc(Matcher<const char*>(StrEq("value2")))).WillOnce(ReturnRef(desc_mock));
+	EXPECT_CALL(data_store_mock, desc(Matcher<const char*>(StrEq("value1")))).WillOnce(ReturnRef(desc_mock));
+	EXPECT_CALL(data_store_mock, desc(Matcher<const char*>(StrEq("value2")))).WillOnce(ReturnRef(desc_mock));
+	EXPECT_CALL(context_mock, data()).Times(2).WillRepeatedly(ReturnRef(data_store_mock));
 	Expression exp{"(${value1} + $value2) * 2"};
 	ASSERT_EQ((value1 + value2) * 2, exp.to_long(context_mock));
 }
diff --git a/tests/mocks/context_mock.h b/tests/mocks/context_mock.h
index e08f9c922..5d0f2290e 100644
--- a/tests/mocks/context_mock.h
+++ b/tests/mocks/context_mock.h
@@ -33,34 +33,13 @@
 
 
 struct MockContext : public PDI::Context {
-	MOCK_METHOD1(desc, PDI::Data_descriptor&(const std::string&));
-	MOCK_METHOD1(desc, PDI::Data_descriptor&(const char*));
-	
-	MOCK_METHOD1(BracketOp1, PDI::Data_descriptor&(const std::string&));
-	PDI::Data_descriptor& operator [] (const std::string& str) override
-	{
-		return BracketOp1(str);
-	}
-	
-	MOCK_METHOD1(BracketOp2, PDI::Data_descriptor&(const char*));
-	PDI::Data_descriptor& operator [] (const char* str) override
-	{
-		return BracketOp2(str);
-	}
-	
-	MOCK_METHOD0(begin, PDI::Context::Iterator());
-	MOCK_METHOD0(end, PDI::Context::Iterator());
-	
+	MOCK_METHOD0(data, PDI::Data_store&());
 	MOCK_METHOD1(event, void(const char*));
-	
 	MOCK_CONST_METHOD0(logger, PDI::Logger_sptr());
-	
 	MOCK_METHOD1(datatype, PDI::Datatype_template_uptr(PC_tree_t));
 	MOCK_METHOD2(add_datatype, void(const std::string&, Datatype_template_parser));
 	MOCK_METHOD1(add_init_callback, std::function<void()>(const std::function<void()>&));
-	MOCK_METHOD2(add_data_callback, std::function<void()>(const std::function<void(const std::string&, PDI::Ref)>&, const std::string& name));
 	MOCK_METHOD2(add_event_callback,std::function<void()>(const std::function<void(const std::string&)>&, const std::string& name));
-	MOCK_METHOD2(add_empty_desc_access_callback, std::function<void()>(const std::function<void(const std::string&)>&, const std::string& name));
 };
 
 
diff --git a/tests/mocks/data_store_mock.h b/tests/mocks/data_store_mock.h
new file mode 100644
index 000000000..4924b0e69
--- /dev/null
+++ b/tests/mocks/data_store_mock.h
@@ -0,0 +1,61 @@
+/*******************************************************************************
+ * Copyright (C) 201 Institute of Bioorganic Chemistry Polish Academy of Science (PSNC)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * * Neither the name of CEA nor the names of its contributors may be used to
+ *   endorse or promote products derived from this software without specific
+ *   prior written permission.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ ******************************************************************************/
+
+#ifndef PDI_DATA_STORE_MOCK_H_
+#define PDI_DATA_STORE_MOCK_H_
+
+#include <memory>
+#include <gmock/gmock.h>
+#include <pdi/data_store.h>
+#include <pdi/datatype_template.h>
+#include <pdi/plugin.h>
+
+
+struct MockDataStore : public PDI::Data_store {
+	MOCK_METHOD1(desc, PDI::Data_descriptor&(const std::string&));
+	MOCK_METHOD1(desc, PDI::Data_descriptor&(const char*));
+	
+	MOCK_METHOD1(BracketOp1, PDI::Data_descriptor&(const std::string&));
+	PDI::Data_descriptor& operator [] (const std::string& str) override
+	{
+		return BracketOp1(str);
+	}
+	
+	MOCK_METHOD1(BracketOp2, PDI::Data_descriptor&(const char*));
+	PDI::Data_descriptor& operator [] (const char* str) override
+	{
+		return BracketOp2(str);
+	}
+	
+	MOCK_METHOD0(begin, PDI::Data_store::Iterator());
+	MOCK_METHOD0(end, PDI::Data_store::Iterator());
+	
+	MOCK_METHOD2(add_data_callback, std::function<void()>(const std::function<void(const std::string&, PDI::Ref)>&, const std::string& name));
+	MOCK_METHOD2(add_empty_desc_access_callback, std::function<void()>(const std::function<void(const std::string&)>&, const std::string& name));
+	MOCK_METHOD2(trigger_data_callbacks, void(const std::string& name, PDI::Ref));
+	MOCK_METHOD1(trigger_empty_desc_access_callbacks, void(const std::string& name));
+};
+
+
+#endif //PDI_DATA_STORE_MOCK_H_
diff --git a/tests/mocks/global_context_mock.h b/tests/mocks/global_context_mock.h
index e9e9646c8..b31148dc1 100644
--- a/tests/mocks/global_context_mock.h
+++ b/tests/mocks/global_context_mock.h
@@ -39,32 +39,12 @@ struct MockGlobalContext : public PDI::Global_context {
 	{}
 	PDI::Paraconf_wrapper fw;
 	
-	MOCK_METHOD1(desc, PDI::Data_descriptor&(const std::string&));
-	MOCK_METHOD1(desc, PDI::Data_descriptor&(const char*));
-	
-	MOCK_METHOD1(BracketOp1, PDI::Data_descriptor&(const std::string&));
-	PDI::Data_descriptor& operator [] (const std::string& str) override
-	{
-		return BracketOp1(str);
-	}
-	
-	MOCK_METHOD1(BracketOp2, PDI::Data_descriptor&(const char*));
-	PDI::Data_descriptor& operator [] (const char* str) override
-	{
-		return BracketOp2(str);
-	}
-	
-	MOCK_METHOD0(begin, PDI::Context::Iterator());
-	MOCK_METHOD0(end, PDI::Context::Iterator());
-	
 	MOCK_METHOD1(event, void(const char*));
 	
 	MOCK_METHOD1(datatype, PDI::Datatype_template_uptr(PC_tree_t));
 	MOCK_METHOD2(add_datatype, void(const std::string&, Datatype_template_parser));
 	MOCK_METHOD1(add_init_callback, std::function<void()>(const std::function<void()>&));
-	MOCK_METHOD2(add_data_callback, std::function<void()>(const std::function<void(const std::string&, PDI::Ref)>&, const std::string& name));
 	MOCK_METHOD2(add_event_callback,std::function<void()>(const std::function<void(const std::string&)>&, const std::string& name));
-	MOCK_METHOD2(add_empty_desc_access_callback, std::function<void()>(const std::function<void(const std::string&)>&, const std::string& name));
 };
 
 
-- 
GitLab


From 4fd740ef3b298a2bb293f8272989425921bd2e72 Mon Sep 17 00:00:00 2001
From: Tomasz Paluszkiewicz <tomaszp@man.poznan.pl>
Date: Fri, 5 Jul 2019 14:17:06 +0200
Subject: [PATCH 2/3] Move named callbacks to Data_descriptor

---
 include/pdi/data_descriptor.h                 |  32 +--
 include/pdi/data_store.h                      |  29 +--
 plugins/flowvr/src/module.h                   |  20 +-
 .../src/payloads/payload_button_event.h       |   8 +-
 plugins/flowvr/src/payloads/payload_chunk.h   |   4 +-
 plugins/flowvr/src/payloads/payload_data.h    |  16 +-
 .../flowvr/src/payloads/payload_mouse_event.h |  16 +-
 plugins/flowvr/src/port.h                     |   4 +-
 plugins/flowvr/src/stamps/stamp_desc.h        |  20 +-
 plugins/flowvr/src/trace.h                    |   4 +-
 plugins/user_code/user_code.cxx               |   4 +-
 src/data_descriptor.cxx                       |  12 +-
 src/data_descriptor_impl.cxx                  | 104 +++++++++-
 src/data_descriptor_impl.h                    |  28 ++-
 src/data_store.cxx                            |   2 +
 src/data_store_impl.cxx                       | 123 ++---------
 src/data_store_impl.h                         |  23 +--
 tests/PDI_data_descriptor.cxx                 | 194 +++++++++++++++++-
 tests/PDI_data_store.cxx                      | 138 -------------
 tests/mocks/data_descriptor_mock.h            |   3 +
 tests/mocks/data_store_mock.h                 |   6 +-
 21 files changed, 409 insertions(+), 381 deletions(-)

diff --git a/include/pdi/data_descriptor.h b/include/pdi/data_descriptor.h
index 3af19680c..401aff8e6 100644
--- a/include/pdi/data_descriptor.h
+++ b/include/pdi/data_descriptor.h
@@ -25,6 +25,8 @@
 #ifndef PDI_DATA_DESCRIPTOR_H_
 #define PDI_DATA_DESCRIPTOR_H_
 
+#include <functional>
+
 #include <paraconf.h>
 
 #include <pdi/pdi_fwd.h>
@@ -36,20 +38,6 @@ namespace PDI {
 
 class PDI_EXPORT Data_descriptor
 {
-protected:
-	/** Lets implementation of the Data_descriptor to call trigger_data_callbacks
-	 *  in Data_store class
-	 *
-	 * \param data_store the data store to trigger data callbacks on
-	 */
-	void trigger_data_callbacks(Data_store& data_store);
-	
-	/** Lets implementation of the Data_descriptor to call trigger_empty_desc_access_callbacks
-	 *  in Data_store class
-	 *
-	 * \param data_store the data store to trigger empty_desc_access callbacks on
-	 */
-	void trigger_empty_desc_access_callbacks(Data_store& data_store);
 public:
 	virtual ~Data_descriptor();
 	
@@ -117,6 +105,22 @@ public:
 	 */
 	virtual void* reclaim() = 0;
 	
+	/** Adds new data callback to descriptor
+	 *
+	 * \param[in] callback function to call when data is being available
+	 *
+	 * \return function that removes callback
+	 */
+	virtual std::function<void()> add_data_callback(const std::function<void(const std::string&, Ref)>& callback) = 0;
+	
+	/** Adds new empty desc access callback to descriptor
+	 *
+	 * \param[in] callback function to call when event is called
+	 *
+	 * \return function that removes callback
+	 */
+	virtual std::function<void()> add_empty_desc_access_callback(const std::function<void(const std::string&)>& callback) = 0;
+	
 }; // class Data_descriptor
 
 } // namespace PDI
diff --git a/include/pdi/data_store.h b/include/pdi/data_store.h
index 3e7219bb2..97e5b59d9 100644
--- a/include/pdi/data_store.h
+++ b/include/pdi/data_store.h
@@ -27,12 +27,9 @@
 #define PDI_DATA_STORE_H_
 
 #include <functional>
-#include <list>
-#include <map>
 #include <memory>
 #include <string>
 #include <unordered_map>
-#include <unordered_set>
 
 #include <pdi/pdi_fwd.h>
 #include <pdi/data_descriptor.h>
@@ -45,8 +42,6 @@ namespace PDI {
 class PDI_EXPORT Data_store
 {
 public:
-	friend class Data_descriptor;
-	
 	/** An iterator used to go through the descriptor store.
 	 */
 	class Iterator
@@ -68,20 +63,6 @@ protected:
 	
 	Iterator get_iterator(std::unordered_map<std::string, std::unique_ptr<Data_descriptor>>::iterator&& data);
 	
-private:
-	/** Triggers data callbacks
-	 *
-	 * \param[in] name name of the descriptor that triggers callbacks
-	 * \param[in] ref reference to the value of the data behind the descriptor
-	 */
-	virtual void trigger_data_callbacks(const std::string& name, Ref ref) = 0;
-	
-	/** Triggers empty_desc_access callbacks
-	 *
-	 * \param[in] name name of the descriptor that triggers callbacks
-	 */
-	virtual void trigger_empty_desc_access_callbacks(const std::string& name) = 0;
-	
 public:
 	virtual ~Data_store();
 	
@@ -109,23 +90,21 @@ public:
 	 */
 	virtual Iterator end() = 0;
 	
-	/** Adds new data callback to context
+	/** Adds new data callback to data store
 	 *
 	 * \param[in] callback function to call when data is being available
-	 * \param[in] name the name of the data on which call the callback, if not specified it's called on any data
 	 *
 	 * \return function that removes callback
 	 */
-	virtual std::function<void()> add_data_callback(const std::function<void(const std::string&, Ref)>& callback, const std::string& name = {}) = 0;
+	virtual std::function<void()> add_data_callback(const std::function<void(const std::string&, Ref)>& callback) = 0;
 	
-	/** Adds new empty desc access callback to context
+	/** Adds new empty desc access callback to data store
 	 *
 	 * \param[in] callback function to call when event is called
-	 * \param[in] name the name of the data on which call the callback, if not specified it's called on any data
 	 *
 	 * \return function that removes callback
 	 */
-	virtual std::function<void()> add_empty_desc_access_callback(const std::function<void(const std::string&)>& callback, const std::string& name = {}) = 0;
+	virtual std::function<void()> add_empty_desc_access_callback(const std::function<void(const std::string&)>& callback) = 0;
 	
 };
 
diff --git a/plugins/flowvr/src/module.h b/plugins/flowvr/src/module.h
index be3e3cfde..101d16077 100644
--- a/plugins/flowvr/src/module.h
+++ b/plugins/flowvr/src/module.h
@@ -83,9 +83,9 @@ private:
 		if (!PC_status(wait_on_data_node)) {
 			std::string wait_data = PDI::to_string(wait_on_data_node);
 			context().logger()->debug("(FlowVR) Module: wait_on_data = {}", wait_data);
-			context().data().add_data_callback([this](const std::string& name, PDI::Ref ref) {
+			context().data()[wait_data].add_data_callback([this](const std::string& name, PDI::Ref ref) {
 				this->wait(name, ref);
-			}, wait_data);
+			});
 		}
 		
 		PC_tree_t wait_on_node = PC_get(config, ".wait_on");
@@ -113,15 +113,15 @@ private:
 				int nb_status = PDI::len(status_node);
 				for (int status_id = 0; status_id < nb_status; status_id++) {
 					std::string status_data = PDI::to_string(PC_get(status_node, "[%d]", status_id));
-					context().data().add_data_callback([this](const std::string& name, PDI::Ref ref) {
+					context().data()[status_data].add_data_callback([this](const std::string& name, PDI::Ref ref) {
 						this->status(ref);
-					}, status_data);
+					});
 				}
 			} else {
 				std::string status_data = PDI::to_string(status_node);
-				context().data().add_data_callback([this](const std::string& name, PDI::Ref ref) {
+				context().data()[status_data].add_data_callback([this](const std::string& name, PDI::Ref ref) {
 					this->status(ref);
-				}, status_data);
+				});
 			}
 		}
 		
@@ -150,18 +150,18 @@ private:
 			if (!PC_status(get_rank_node)) {
 				std::string get_parallel_rank_data = PDI::to_string(get_rank_node);
 				context().logger()->debug("(FlowVR) Module: Parallel rank = {}", get_parallel_rank_data);
-				context().data().add_data_callback([this](const std::string& name, PDI::Ref ref) {
+				context().data()[get_parallel_rank_data].add_data_callback([this](const std::string& name, PDI::Ref ref) {
 					this->get_parallel_rank(name, ref);
-				}, get_parallel_rank_data);
+				});
 			}
 			
 			PC_tree_t get_size_node = PC_get(parallel_node, ".get_size");
 			if (!PC_status(get_size_node)) {
 				std::string get_parallel_size_data = PDI::to_string(get_size_node);
 				context().logger()->debug("(FlowVR) Module: Parallel size = {}", get_parallel_size_data);
-				context().data().add_data_callback([this](const std::string& name, PDI::Ref ref) {
+				context().data()[get_parallel_size_data].add_data_callback([this](const std::string& name, PDI::Ref ref) {
 					this->get_parallel_size(name, ref);
-				}, get_parallel_size_data);
+				});
 			}
 		}
 		
diff --git a/plugins/flowvr/src/payloads/payload_button_event.h b/plugins/flowvr/src/payloads/payload_button_event.h
index c16acff4f..92e602691 100644
--- a/plugins/flowvr/src/payloads/payload_button_event.h
+++ b/plugins/flowvr/src/payloads/payload_button_event.h
@@ -146,9 +146,9 @@ public:
 		m_flowvr_input_port{parent_port}
 	{
 		for (const auto& desc_value : m_desc_value_map) {
-			m_ctx.data().add_data_callback([this](const std::string& name, PDI::Ref ref) {
+			m_ctx.data()[desc_value.first].add_data_callback([this](const std::string& name, PDI::Ref ref) {
 				this->data(name, ref);
-			}, desc_value.first);
+			});
 		}
 		m_ctx.logger()->debug("(FlowVR) Input Button Payload ({}): Created", m_name);
 	}
@@ -235,9 +235,9 @@ public:
 		m_flowvr_output_port{parent_port}
 	{
 		for (const auto& desc_value : m_desc_value_map) {
-			m_ctx.data().add_data_callback([this](const std::string& name, PDI::Ref ref) {
+			m_ctx.data()[desc_value.first].add_data_callback([this](const std::string& name, PDI::Ref ref) {
 				this->data(name, ref);
-			}, desc_value.first);
+			});
 		}
 		m_ctx.logger()->debug("(FlowVR) Output Button Payload ({}): Created", m_name);
 	}
diff --git a/plugins/flowvr/src/payloads/payload_chunk.h b/plugins/flowvr/src/payloads/payload_chunk.h
index f77930e9b..ebaad897b 100644
--- a/plugins/flowvr/src/payloads/payload_chunk.h
+++ b/plugins/flowvr/src/payloads/payload_chunk.h
@@ -77,9 +77,9 @@ protected:
 			m_chunk_descs.emplace_back(PDI::to_string(PC_get(chunk_node, ".data")));
 		}
 		for (const std::string& data_name : m_chunk_descs) {
-			m_ctx.data().add_empty_desc_access_callback([this](const std::string& name) {
+			m_ctx.data()[data_name].add_empty_desc_access_callback([this](const std::string& name) {
 				this->empty_desc_access(name);
-			}, data_name);
+			});
 		}
 	}
 	
diff --git a/plugins/flowvr/src/payloads/payload_data.h b/plugins/flowvr/src/payloads/payload_data.h
index 4f078f4f5..21a9f8cce 100644
--- a/plugins/flowvr/src/payloads/payload_data.h
+++ b/plugins/flowvr/src/payloads/payload_data.h
@@ -120,12 +120,12 @@ public:
 	{
 		load_data_size_desc(config);
 		if (!m_data_desc.empty()) {
-			m_ctx.data().add_data_callback([this](const std::string& name, PDI::Ref ref) {
+			m_ctx.data()[m_data_desc].add_data_callback([this](const std::string& name, PDI::Ref ref) {
 				this->copy_data_to_ref(name, ref);
-			}, m_data_desc);
-			m_ctx.data().add_empty_desc_access_callback([this](const std::string& name) {
+			});
+			m_ctx.data()[m_data_desc].add_empty_desc_access_callback([this](const std::string& name) {
 				this->empty_desc_access(name);
-			}, m_data_desc);
+			});
 		}
 		m_ctx.logger()->debug("(FlowVR) Input Data Payload ({}): Created", m_name);
 	}
@@ -295,12 +295,12 @@ public:
 		}
 		
 		if (!m_data_desc.empty()) {
-			m_ctx.data().add_data_callback([this](const std::string& name, PDI::Ref ref) {
+			m_ctx.data()[m_data_desc].add_data_callback([this](const std::string& name, PDI::Ref ref) {
 				this->copy_data_from_ref(name, ref);
-			}, m_data_desc);
-			m_ctx.data().add_empty_desc_access_callback([this](const std::string& name) {
+			});
+			m_ctx.data()[m_data_desc].add_empty_desc_access_callback([this](const std::string& name) {
 				this->empty_desc_access(name);
-			}, m_data_desc);
+			});
 		}
 		m_ctx.logger()->debug("(FlowVR) Output Data Payload ({}): Created", m_name);
 	}
diff --git a/plugins/flowvr/src/payloads/payload_mouse_event.h b/plugins/flowvr/src/payloads/payload_mouse_event.h
index 0247f4357..025029b69 100644
--- a/plugins/flowvr/src/payloads/payload_mouse_event.h
+++ b/plugins/flowvr/src/payloads/payload_mouse_event.h
@@ -143,13 +143,13 @@ public:
 		Payload_mouse_event{ctx, name, config, parent_port},
 		m_flowvr_input_port{parent_port}
 	{
-		m_ctx.data().add_data_callback([this](const std::string& name, PDI::Ref ref) {
+		m_ctx.data()[m_desc_pos_xy.first].add_data_callback([this](const std::string& name, PDI::Ref ref) {
 			this->data_pos_xy(name, ref);
-		}, m_desc_pos_xy.first);
+		});
 		for (const auto& desc_value : m_desc_value_map) {
-			m_ctx.data().add_data_callback([this](const std::string& name, PDI::Ref ref) {
+			m_ctx.data()[desc_value.first].add_data_callback([this](const std::string& name, PDI::Ref ref) {
 				this->data(name, ref);
-			}, desc_value.first);
+			});
 		}
 		m_ctx.logger()->debug("(FlowVR) Input Mouse Payload ({}): Created", m_name);
 	}
@@ -268,13 +268,13 @@ public:
 		Payload_mouse_event{ctx, name, config, parent_port},
 		m_flowvr_output_port{parent_port}
 	{
-		m_ctx.data().add_data_callback([this](const std::string& name, PDI::Ref ref) {
+		m_ctx.data()[m_desc_pos_xy.first].add_data_callback([this](const std::string& name, PDI::Ref ref) {
 			this->data_pos_xy(name, ref);
-		}, m_desc_pos_xy.first);
+		});
 		for (const auto& desc_value : m_desc_value_map) {
-			m_ctx.data().add_data_callback([this](const std::string& name, PDI::Ref ref) {
+			m_ctx.data()[desc_value.first].add_data_callback([this](const std::string& name, PDI::Ref ref) {
 				this->data(name, ref);
-			}, desc_value.first);
+			});
 		}
 		m_ctx.logger()->debug("(FlowVR) Output Mouse Payload ({}): Created", m_name);
 	}
diff --git a/plugins/flowvr/src/port.h b/plugins/flowvr/src/port.h
index 37558f9b2..80ee72477 100644
--- a/plugins/flowvr/src/port.h
+++ b/plugins/flowvr/src/port.h
@@ -63,9 +63,9 @@ protected:
 	{
 		PC_tree_t isConnected_node = PC_get(config, ".isConnected");
 		if (!PC_status(isConnected_node)) {
-			m_ctx.data().add_data_callback([this](const std::string& name, PDI::Ref ref) {
+			m_ctx.data()[PDI::to_string(isConnected_node)].add_data_callback([this](const std::string& name, PDI::Ref ref) {
 				this->get_status(name, ref);
-			}, PDI::to_string(isConnected_node));
+			});
 		}
 	}
 	
diff --git a/plugins/flowvr/src/stamps/stamp_desc.h b/plugins/flowvr/src/stamps/stamp_desc.h
index 45ff82d76..8dfc14460 100644
--- a/plugins/flowvr/src/stamps/stamp_desc.h
+++ b/plugins/flowvr/src/stamps/stamp_desc.h
@@ -58,9 +58,9 @@ public:
 		} else {
 			m_stamp_info = new flowvr::StampInfo(m_name, flowvr::TypeInt::create());
 		}
-		m_ctx.data().add_data_callback([this](const std::string& name, PDI::Ref ref) {
+		m_ctx.data()[data_desc].add_data_callback([this](const std::string& name, PDI::Ref ref) {
 			this->data(name, ref);
-		}, data_desc);
+		});
 		m_ctx.logger()->debug("(FlowVR) Int STAMP ({}): Created", m_name);
 	}
 	
@@ -109,9 +109,9 @@ public:
 		Stamp_base{ctx, parent_port, name}
 	{
 		m_stamp_info = new flowvr::StampInfo(m_name, flowvr::TypeFloat::create());
-		m_ctx.data().add_data_callback([this](const std::string& name, PDI::Ref ref) {
+		m_ctx.data()[data_desc].add_data_callback([this](const std::string& name, PDI::Ref ref) {
 			this->data(name, ref);
-		}, data_desc);
+		});
 		m_ctx.logger()->debug("(FlowVR) Float STAMP ({}): Created", m_name);
 	}
 	
@@ -164,9 +164,9 @@ public:
 		} else {
 			m_stamp_info = new flowvr::StampInfo(m_name, flowvr::TypeString::create());
 		}
-		m_ctx.data().add_data_callback([this](const std::string& name, PDI::Ref ref) {
+		m_ctx.data()[data_desc].add_data_callback([this](const std::string& name, PDI::Ref ref) {
 			this->data(name, ref);
-		}, data_desc);
+		});
 		m_ctx.logger()->debug("(FlowVR) String STAMP ({}): Created", m_name);
 	}
 	
@@ -216,9 +216,9 @@ public:
 		m_value(size)
 	{
 		m_stamp_info = new flowvr::StampInfo(m_name, flowvr::TypeArray::create(size, flowvr::TypeInt::create()));
-		m_ctx.data().add_data_callback([this](const std::string& name, PDI::Ref ref) {
+		m_ctx.data()[data_desc].add_data_callback([this](const std::string& name, PDI::Ref ref) {
 			this->data(name, ref);
-		}, data_desc);
+		});
 		m_ctx.logger()->debug("(FlowVR) Int array STAMP ({}): Created with size = {}", m_name, size);
 	}
 	
@@ -272,9 +272,9 @@ public:
 		m_value(size)
 	{
 		m_stamp_info = new flowvr::StampInfo(m_name, flowvr::TypeArray::create(size, flowvr::TypeFloat::create()));
-		m_ctx.data().add_data_callback([this](const std::string& name, PDI::Ref ref) {
+		m_ctx.data()[data_desc].add_data_callback([this](const std::string& name, PDI::Ref ref) {
 			this->data(name, ref);
-		}, data_desc);
+		});
 		m_ctx.logger()->debug("(FlowVR) Float array STAMP ({}): Created with size = {}", m_name, size);
 	}
 	
diff --git a/plugins/flowvr/src/trace.h b/plugins/flowvr/src/trace.h
index 180004263..92059d266 100644
--- a/plugins/flowvr/src/trace.h
+++ b/plugins/flowvr/src/trace.h
@@ -93,9 +93,9 @@ public:
 	{
 		load_on_data(config);
 		load_trace(ctx, name);
-		ctx.data().add_data_callback([this](const std::string& name, PDI::Ref ref) {
+		ctx.data()[m_on_data].add_data_callback([this](const std::string& name, PDI::Ref ref) {
 			this->write(name, ref);
-		}, m_on_data);
+		});
 		
 		ctx.logger()->debug("(FlowVR) Trace ({}): On_data: {} ", name, m_on_data);
 	}
diff --git a/plugins/user_code/user_code.cxx b/plugins/user_code/user_code.cxx
index 91bfe4cca..8a3fd5e78 100644
--- a/plugins/user_code/user_code.cxx
+++ b/plugins/user_code/user_code.cxx
@@ -209,9 +209,9 @@ struct user_code_plugin: Plugin {
 			opt_each(datas, [&](PC_tree_t one_data) {
 				each(one_data, [&](PC_tree_t function_name, PC_tree_t parameters) {
 					Trigger data_trigger{to_string(function_name), parameters};
-					ctx.data().add_data_callback([&ctx, data_trigger](const std::string& name, Ref ref) mutable {
+					ctx.data()[to_string(data_name)].add_data_callback([&ctx, data_trigger](const std::string& name, Ref ref) mutable {
 						data_trigger.call(ctx);
-					}, to_string(data_name));
+					});
 				});
 			});
 		});
diff --git a/src/data_descriptor.cxx b/src/data_descriptor.cxx
index 61bfd3d79..fd77c87b2 100644
--- a/src/data_descriptor.cxx
+++ b/src/data_descriptor.cxx
@@ -22,23 +22,13 @@
  * THE SOFTWARE.
  ******************************************************************************/
 
-#include "pdi/data_store.h"
+#include "config.h"
 
 #include "pdi/data_descriptor.h"
 
 
 namespace PDI {
 
-void Data_descriptor::trigger_data_callbacks(Data_store& data_store)
-{
-	data_store.trigger_data_callbacks(name(), ref());
-}
-
-void Data_descriptor::trigger_empty_desc_access_callbacks(Data_store& data_store)
-{
-	data_store.trigger_empty_desc_access_callbacks(name());
-}
-
 Data_descriptor::~Data_descriptor() = default;
 
 } // namespace PDI
diff --git a/src/data_descriptor_impl.cxx b/src/data_descriptor_impl.cxx
index 9e79289c9..3c1171bb9 100644
--- a/src/data_descriptor_impl.cxx
+++ b/src/data_descriptor_impl.cxx
@@ -24,10 +24,6 @@
 
 #include "config.h"
 
-#include <functional>
-#include <iostream>
-#include <memory>
-
 #include <spdlog/spdlog.h>
 
 #include "pdi/context.h"
@@ -43,10 +39,15 @@
 namespace PDI {
 
 using std::exception;
+using std::function;
+using std::list;
 using std::nothrow;
+using std::reference_wrapper;
 using std::stack;
 using std::string;
+using std::to_string;
 using std::unique_ptr;
+using std::vector;
 
 struct Data_descriptor_impl::Ref_holder {
 
@@ -75,14 +76,15 @@ struct Data_descriptor_impl::Ref_holder::Impl: Data_descriptor_impl::Ref_holder
 	
 };
 
-
-Data_descriptor_impl::Data_descriptor_impl(Context& ctx, const char* name):
+Data_descriptor_impl::Data_descriptor_impl(Context& ctx, const list<function<void(const string&, Ref)>>& any_data_callbacks,
+    const list<function<void(const string&)>>& any_empty_desc_access_callbacks, const char* name):
 	m_context{ctx},
+	m_any_data_callbacks{any_data_callbacks},
+	m_any_empty_desc_access_callbacks{any_empty_desc_access_callbacks},
 	m_type{UNDEF_TYPE.clone_type()},
 	m_name{name},
 	m_metadata{false}
-{
-}
+{}
 
 Data_descriptor_impl::Data_descriptor_impl(Data_descriptor_impl&&) = default;
 
@@ -143,8 +145,40 @@ Ref Data_descriptor_impl::ref()
 {
 	assert((!metadata() || !m_refs.empty()) && "metadata descriptors should always keep a placeholder");
 	if (m_refs.empty()) {
+		vector<reference_wrapper<const function<void(const string&)>>> empty_desc_callbacks;
+		//add descriptor callbacks
+		for (auto it = m_empty_desc_access_callbacks.begin(); it != m_empty_desc_access_callbacks.end(); it++) {
+			empty_desc_callbacks.emplace_back(std::cref(*it));
+		}
+		//add data store callbacks
+		for (auto it = m_any_empty_desc_access_callbacks.begin(); it != m_any_empty_desc_access_callbacks.end(); it++) {
+			empty_desc_callbacks.emplace_back(std::cref(*it));
+		}
 		//trigger empty desc callbacks
-		Data_descriptor::trigger_empty_desc_access_callbacks(m_context.data());
+		vector<Error> errors;
+		for (const function<void(const string&)>& callback : empty_desc_callbacks) {
+			try {
+				callback(name());
+				//TODO: remove the faulty plugin in case of error?
+			} catch (const Error& e) {
+				errors.emplace_back(e);
+			} catch (const exception& e) {
+				errors.emplace_back(PDI_ERR_SYSTEM, e.what());
+			} catch (...) {
+				errors.emplace_back(PDI_ERR_SYSTEM, "Not std::exception based error");
+			}
+		}
+		
+		if (!errors.empty()) {
+			if (1 == errors.size()) {
+				throw Error{errors.front().status(), "Error while triggering empty desc access `%s': %s", name().c_str(), errors.front().what()};
+			}
+			string errmsg = "Multiple (" + to_string(errors.size()) + ") errors while triggering empty desc access `" + name() + "':\n";
+			for (auto&& err: errors) {
+				errmsg += string(err.what()) + "\n";
+			}
+			throw Error{PDI_ERR_SYSTEM, "%s", errmsg.c_str()};
+		}
 		
 		//at least one plugin should share a Ref
 		if (m_refs.empty()) {
@@ -208,8 +242,40 @@ void* Data_descriptor_impl::share(Ref data_ref, bool read, bool write)
 		throw Error{PDI_ERR_RIGHT, "Unable to grant requested rights"};
 	}
 	
+	vector<reference_wrapper<const function<void(const string&, Ref)>>> data_callbacks;
+	//add descriptor callbacks
+	for (auto it = m_data_callbacks.begin(); it != m_data_callbacks.end(); it++) {
+		data_callbacks.emplace_back(std::cref(*it));
+	}
+	//add data store callbacks
+	for (auto it = m_any_data_callbacks.begin(); it != m_any_data_callbacks.end(); it++) {
+		data_callbacks.emplace_back(std::cref(*it));
+	}
 	//trigger data callbacks
-	Data_descriptor::trigger_data_callbacks(m_context.data());
+	vector<Error> errors;
+	for (const function<void(const string&, Ref)>& callback : data_callbacks) {
+		try {
+			callback(name(), ref());
+			//TODO: remove the faulty plugin in case of error?
+		} catch (const Error& e) {
+			errors.emplace_back(e);
+		} catch (const exception& e) {
+			errors.emplace_back(PDI_ERR_SYSTEM, e.what());
+		} catch (...) {
+			errors.emplace_back(PDI_ERR_SYSTEM, "Not std::exception based error");
+		}
+	}
+	
+	if (!errors.empty()) {
+		if (1 == errors.size()) {
+			throw Error{errors.front().status(), "Error while triggering empty desc access `%s': %s", name().c_str(), errors.front().what()};
+		}
+		string errmsg = "Multiple (" + to_string(errors.size()) + ") errors while triggering empty desc access `" + name() + "':\n";
+		for (auto&& err: errors) {
+			errmsg += string(err.what()) + "\n";
+		}
+		throw Error{PDI_ERR_SYSTEM, "%s", errmsg.c_str()};
+	}
 	
 	assert((!metadata() || !m_refs.empty()) && "metadata descriptors should always keep a placeholder");
 	return result;
@@ -251,4 +317,22 @@ void* Data_descriptor_impl::reclaim()
 	return oldref.release();
 }
 
+function<void()> Data_descriptor_impl::add_data_callback(const function<void(const string&, Ref)>& callback)
+{
+	m_data_callbacks.emplace_back(callback);
+	auto it = --m_data_callbacks.end();
+	return [it, this]() {
+		this->m_data_callbacks.erase(it);
+	};
+}
+
+function<void()> Data_descriptor_impl::add_empty_desc_access_callback(const function<void(const string&)>& callback)
+{
+	m_empty_desc_access_callbacks.emplace_back(callback);
+	auto it = --m_empty_desc_access_callbacks.end();
+	return [it, this]() {
+		this->m_empty_desc_access_callbacks.erase(it);
+	};
+}
+
 } // namespace PDI
diff --git a/src/data_descriptor_impl.h b/src/data_descriptor_impl.h
index 99621a7ca..afa76a175 100644
--- a/src/data_descriptor_impl.h
+++ b/src/data_descriptor_impl.h
@@ -51,6 +51,26 @@ class PDI_EXPORT Data_descriptor_impl : public Data_descriptor
 	/// The context this descriptor is part of
 	Context& m_context;
 	
+	/// Reference to data callbacks that triggers when any data becomes available
+	const std::list<std::function<void(const std::string&, Ref)>>& m_any_data_callbacks;
+
+	/// Reference to empty desc access callbacks that triggers when any empty desc access occurs
+	const std::list<std::function<void(const std::string&)>>& m_any_empty_desc_access_callbacks;
+
+    /** 
+	 *  Callbacks called when data is available
+	 * 
+	 *  This must be a list, because valid iterators are needed to properly remove the callback by plugin
+	 */
+	std::list<std::function<void(const std::string&, Ref)>> m_data_callbacks;
+
+    /** 
+	 *  Callbacks called on empty desc access
+	 * 
+	 *  This must be a list, because valid iterators are needed to properly remove the callback by plugin
+	 */
+	std::list<std::function<void(const std::string&)>> m_empty_desc_access_callbacks;
+
 	/// References to the values of this descriptor
 	std::stack<std::unique_ptr<Ref_holder>> m_refs;
 	
@@ -60,10 +80,10 @@ class PDI_EXPORT Data_descriptor_impl : public Data_descriptor
 	
 	bool m_metadata;
 	
-	
 	/** Create an empty descriptor
 	 */
-	Data_descriptor_impl(Context& ctx, const char* name);
+	Data_descriptor_impl(Context& ctx, const std::list<std::function<void(const std::string&, Ref)>>& any_data_callbacks, 
+		const std::list<std::function<void(const std::string&)>>& any_empty_desc_access_callbacks, const char* name);
 	
 	Data_descriptor_impl(const Data_descriptor_impl&) = delete;
 	
@@ -98,6 +118,10 @@ public:
 	
 	void* reclaim() override;
 	
+	std::function<void()> add_data_callback(const std::function<void(const std::string&, Ref)>& callback) override;
+
+	std::function<void()> add_empty_desc_access_callback(const std::function<void(const std::string&)>& callback) override;
+
 }; // class Data_descriptor
 
 } // namespace PDI
diff --git a/src/data_store.cxx b/src/data_store.cxx
index 41b8603b3..3fa559ea8 100644
--- a/src/data_store.cxx
+++ b/src/data_store.cxx
@@ -23,6 +23,8 @@
  * THE SOFTWARE.
  ******************************************************************************/
 
+#include "config.h"
+
 #include "pdi/data_store.h"
 
 
diff --git a/src/data_store_impl.cxx b/src/data_store_impl.cxx
index 3a6989ddb..dca49507a 100644
--- a/src/data_store_impl.cxx
+++ b/src/data_store_impl.cxx
@@ -23,8 +23,8 @@
  * THE SOFTWARE.
  ******************************************************************************/
 
-#include <iostream>
-#include <memory>
+#include "config.h"
+
 #include <vector>
 
 #include "pdi/paraconf_wrapper.h"
@@ -39,7 +39,6 @@
 
 namespace PDI {
 
-
 using std::exception;
 using std::function;
 using std::move;
@@ -57,7 +56,9 @@ Data_store_impl::Data_store_impl(Context& ctx):
 
 Data_descriptor& Data_store_impl::desc(const char* name)
 {
-	return *(m_descriptors.emplace(name, unique_ptr<Data_descriptor> {new Data_descriptor_impl{m_context, name}}).first->second);
+	return *(m_descriptors.emplace(name, unique_ptr<Data_descriptor> {
+		new Data_descriptor_impl{m_context, m_data_callbacks, m_empty_desc_access_callbacks, name}
+	}).first->second);
 }
 
 Data_descriptor& Data_store_impl::desc(const string& name)
@@ -85,112 +86,22 @@ Data_store::Iterator Data_store_impl::end()
 	return Data_store::get_iterator(m_descriptors.end());
 }
 
-std::function<void()> Data_store_impl::add_data_callback(const std::function<void(const std::string&, Ref)>& callback, const std::string& name)
-{
-	if (name.empty()) {
-		m_data_callbacks.emplace_back(callback);
-		auto it = --m_data_callbacks.end();
-		return [it, this]() {
-			this->m_data_callbacks.erase(it);
-		};
-	} else {
-		auto it = m_named_data_callbacks.emplace(name, callback);
-		return [it, this]() {
-			this->m_named_data_callbacks.erase(it);
-		};
-	}
-}
-
-std::function<void()> Data_store_impl::add_empty_desc_access_callback(const std::function<void(const std::string&)>& callback, const std::string& name)
-{
-	if (name.empty()) {
-		m_empty_desc_access_callbacks.emplace_back(callback);
-		auto it = --m_empty_desc_access_callbacks.end();
-		return [it, this]() {
-			this->m_empty_desc_access_callbacks.erase(it);
-		};
-	} else {
-		auto it = m_named_empty_desc_access_callbacks.emplace(name, callback);
-		return [it, this]() {
-			this->m_named_empty_desc_access_callbacks.erase(it);
-		};
-	}
-}
-
-void Data_store_impl::trigger_data_callbacks(const string& name, Ref ref)
+std::function<void()> Data_store_impl::add_data_callback(const std::function<void(const std::string&, Ref)>& callback)
 {
-	vector<reference_wrapper<const function<void(const string&, Ref)>>> data_callbacks;
-	//add named callbacks
-	auto callback_it_pair = m_named_data_callbacks.equal_range(name);
-	for (auto it = callback_it_pair.first; it != callback_it_pair.second; it++) {
-		data_callbacks.emplace_back(std::cref(it->second));
-	}
-	//add the unnamed callbacks
-	for (auto it = m_data_callbacks.begin(); it != m_data_callbacks.end(); it++) {
-		data_callbacks.emplace_back(std::cref(*it));
-	}
-	//call gathered callbacks
-	vector<Error> errors;
-	for (const function<void(const string&, Ref)>& callback : data_callbacks) {
-		try {
-			callback(name, ref);
-			//TODO: remove the faulty plugin in case of error?
-		} catch (const Error& e) {
-			errors.emplace_back(e);
-		} catch (const exception& e) {
-			errors.emplace_back(PDI_ERR_SYSTEM, e.what());
-		} catch (...) {
-			errors.emplace_back(PDI_ERR_SYSTEM, "Not std::exception based error");
-		}
-	}
-	if (!errors.empty()) {
-		if (1 == errors.size()) {
-			throw Error{errors.front().status(), "Error while triggering data share `%s': %s", name, errors.front().what()};
-		}
-		string errmsg = "Multiple (" + to_string(errors.size()) + ") errors while triggering data share `" + name + "':\n";
-		for (auto&& err: errors) {
-			errmsg += string(err.what()) + "\n";
-		}
-		throw Error{PDI_ERR_SYSTEM, "%s", errmsg.c_str()};
-	}
+	m_data_callbacks.emplace_back(callback);
+	auto it = --m_data_callbacks.end();
+	return [it, this]() {
+		this->m_data_callbacks.erase(it);
+	};
 }
 
-void Data_store_impl::trigger_empty_desc_access_callbacks(const string& name)
+std::function<void()> Data_store_impl::add_empty_desc_access_callback(const std::function<void(const std::string&)>& callback)
 {
-	vector<reference_wrapper<const function<void(const string&)>>> empty_desc_callbacks;
-	//add named callbacks
-	auto callback_it_pair = m_named_empty_desc_access_callbacks.equal_range(name);
-	for (auto it = callback_it_pair.first; it != callback_it_pair.second; it++) {
-		empty_desc_callbacks.emplace_back(std::cref(it->second));
-	}
-	//add the unnamed callbacks
-	for (auto it = m_empty_desc_access_callbacks.begin(); it != m_empty_desc_access_callbacks.end(); it++) {
-		empty_desc_callbacks.emplace_back(std::cref(*it));
-	}
-	//call gathered callbacks
-	vector<Error> errors;
-	for (const std::function<void(const std::string&)>& callback : empty_desc_callbacks) {
-		try {
-			callback(name);
-			//TODO: remove the faulty plugin in case of error?
-		} catch (const Error& e) {
-			errors.emplace_back(e);
-		} catch (const exception& e) {
-			errors.emplace_back(PDI_ERR_SYSTEM, e.what());
-		} catch (...) {
-			errors.emplace_back(PDI_ERR_SYSTEM, "Not std::exception based error");
-		}
-	}
-	if (!errors.empty()) {
-		if (1 == errors.size()) {
-			throw Error{errors.front().status(), "Error while triggering empty desc access `%s': %s", name.c_str(), errors.front().what()};
-		}
-		string errmsg = "Multiple (" + to_string(errors.size()) + ") errors while triggering empty desc access `" + name + "':\n";
-		for (auto&& err: errors) {
-			errmsg += string(err.what()) + "\n";
-		}
-		throw Error{PDI_ERR_SYSTEM, "%s", errmsg.c_str()};
-	}
+	m_empty_desc_access_callbacks.emplace_back(callback);
+	auto it = --m_empty_desc_access_callbacks.end();
+	return [it, this]() {
+		this->m_empty_desc_access_callbacks.erase(it);
+	};
 }
 
 }
diff --git a/src/data_store_impl.h b/src/data_store_impl.h
index 7bbe883b7..a21b472ce 100644
--- a/src/data_store_impl.h
+++ b/src/data_store_impl.h
@@ -28,7 +28,6 @@
 
 #include <functional>
 #include <list>
-#include <map>
 #include <memory>
 #include <string>
 #include <unordered_map>
@@ -56,13 +55,6 @@ private:
 	 */
 	std::list<std::function<void(const std::string&, Ref)>> m_data_callbacks;
 
-	/**
-	 *  Callbacks called when specified data is available.
-	 * 
-	 *  This must be an ordered multimap, because valid iterators are needed to properly remove the callback by plugin
-	 */
-	std::multimap<std::string, std::function<void(const std::string&, Ref)>> m_named_data_callbacks;
-
     /** 
 	 *  Callbacks called on any empty desc access
 	 * 
@@ -70,17 +62,6 @@ private:
 	 */
 	std::list<std::function<void(const std::string&)>> m_empty_desc_access_callbacks;
 
-	/**
-	 *  Callbacks called on specified empty desc access
-	 * 
-	 *  This must be an ordered multimap, because valid iterators are needed to properly remove the callback by plugin
-	 */
-	std::multimap<std::string, std::function<void(const std::string&)>> m_named_empty_desc_access_callbacks;
-
-	void trigger_data_callbacks(const std::string& name, Ref ref) override;
-
-	void trigger_empty_desc_access_callbacks(const std::string& name) override;
-
 public:
     Data_store_impl(Context& ctx);
 	
@@ -96,9 +77,9 @@ public:
 
 	Iterator end() override;
 
-	std::function<void()> add_data_callback(const std::function<void(const std::string&, Ref)>& callback, const std::string& name = {}) override;
+	std::function<void()> add_data_callback(const std::function<void(const std::string&, Ref)>& callback) override;
 	
-	std::function<void()> add_empty_desc_access_callback(const std::function<void(const std::string&)>& callback, const std::string& name = {}) override;
+	std::function<void()> add_empty_desc_access_callback(const std::function<void(const std::string&)>& callback) override;
 
 };
 
diff --git a/tests/PDI_data_descriptor.cxx b/tests/PDI_data_descriptor.cxx
index 416a5138b..abe9ac9db 100644
--- a/tests/PDI_data_descriptor.cxx
+++ b/tests/PDI_data_descriptor.cxx
@@ -44,9 +44,13 @@ using ::testing::Return;
 namespace PDI {
 //handler to private fields of Descriptor
 struct Descriptor_test_handler {
-	static unique_ptr<Data_descriptor> default_desc(MockGlobalContext& mockGlCtx)
+	static unique_ptr<Data_descriptor> default_desc(MockGlobalContext& mockGlCtx, const char* name = "default_desc")
 	{
-		return unique_ptr<Data_descriptor> {new Data_descriptor_impl{mockGlCtx, "default_desc"}};
+		//shouldn't be necessary for these tests
+		static list<function<void(const string&, Ref)>> data_callbacks;
+		static list<function<void(const string&)>> empty_desc_access_callbacks;
+		
+		return unique_ptr<Data_descriptor> {new Data_descriptor_impl{mockGlCtx, data_callbacks, empty_desc_access_callbacks, name}};
 	}
 	
 	static Datatype_uptr desc_get_type(unique_ptr<Data_descriptor>& desc, MockGlobalContext& mockGlCtx)
@@ -343,3 +347,189 @@ TEST_F(DataDescTest, multi_read_share_meta)
 	ptr = Ref_r{this->m_desc_default->ref()}.get();
 	ASSERT_NE(this->array, ptr);
 }
+
+/*
+ * Name:                DataDescTest.add_data_callback
+ *
+ * Tested functions:    PDI::Data_descriptor::add_data_callback
+ *
+ *
+ * Description:         Checks if callback is
+ *                      correctly called on data share.
+ *
+ */
+TEST_F(DataDescTest, add_data_callback)
+{
+	auto desc_x = Descriptor_test_handler::default_desc(this->mockGlCtx, "data_x");
+	auto desc_y = Descriptor_test_handler::default_desc(this->mockGlCtx, "data_y");
+	desc_x->default_type(Scalar_datatype{Scalar_kind::SIGNED, sizeof(int)}.clone_type());
+	desc_y->default_type(Scalar_datatype{Scalar_kind::SIGNED, sizeof(int)}.clone_type());
+	int x = 0;
+	int y = 0;
+	desc_x->add_data_callback([](const std::string& name, Ref ref) {
+		Ref_w ref_write {ref};
+		int* x = static_cast<int*>(ref_write.get());
+		*x += 42;
+		ASSERT_STREQ(name.c_str(), "data_x");
+	});
+	ASSERT_EQ(x, 0);
+	ASSERT_EQ(y, 0);
+	desc_x->share(&x, true, true);
+	desc_x->reclaim();
+	ASSERT_EQ(x, 42);
+	ASSERT_EQ(y, 0);
+}
+
+/*
+ * Name:                DataDescTest.remove_data_callback
+ *
+ * Tested functions:    PDI::Data_descriptor::add_data_callback
+ *
+ *
+ * Description:         Checks if callback is
+ *                      correctly called on data share.
+ *
+ */
+TEST_F(DataDescTest, remove_data_callback)
+{
+	auto desc_x = Descriptor_test_handler::default_desc(this->mockGlCtx, "data_x");
+	auto desc_y = Descriptor_test_handler::default_desc(this->mockGlCtx, "data_y");
+	desc_x->default_type(Scalar_datatype{Scalar_kind::SIGNED, sizeof(int)}.clone_type());
+	desc_y->default_type(Scalar_datatype{Scalar_kind::SIGNED, sizeof(int)}.clone_type());
+	int x = 0;
+	int y = 0;
+	auto erase_x = desc_x->add_data_callback([](const std::string& name, Ref ref) {
+		Ref_w ref_write {ref};
+		int* x = static_cast<int*>(ref_write.get());
+		*x += 42;
+		ASSERT_STREQ(name.c_str(), "data_x");
+	});
+	ASSERT_EQ(x, 0);
+	ASSERT_EQ(y, 0);
+	desc_x->share(&x, true, true);
+	desc_x->reclaim();
+	ASSERT_EQ(x, 42);
+	ASSERT_EQ(y, 0);
+	erase_x();
+	desc_x->share(&x, true, true);
+	desc_x->reclaim();
+	ASSERT_EQ(x, 42);
+	ASSERT_EQ(y, 0);
+}
+
+/*
+ * Name:                DataDescTest.add_remove_data_callback
+ *
+ * Tested functions:    PDI::Data_descriptor::add_data_callback
+ *
+ *
+ * Description:         Checks if named callback is
+ *                      correctly called on share
+ *                      and removes it several times.
+ *
+ */
+TEST_F(DataDescTest, add_remove_named_data_callback)
+{
+	auto desc_x = Descriptor_test_handler::default_desc(this->mockGlCtx, "data_x");
+	auto desc_y = Descriptor_test_handler::default_desc(this->mockGlCtx, "data_y");
+	desc_x->default_type(Scalar_datatype{Scalar_kind::SIGNED, sizeof(int)}.clone_type());
+	desc_y->default_type(Scalar_datatype{Scalar_kind::SIGNED, sizeof(int)}.clone_type());
+	int x = 0;
+	int y = 0;
+	auto erase_x = desc_x->add_data_callback([](const std::string& name, Ref ref) {
+		Ref_w ref_write {ref};
+		int* x = static_cast<int*>(ref_write.get());
+		*x += 42;
+		ASSERT_STREQ(name.c_str(), "data_x");
+	});
+	auto erase_y = desc_y->add_data_callback([](const std::string& name, Ref ref) {
+		Ref_w ref_write {ref};
+		int* y = static_cast<int*>(ref_write.get());
+		*y += 53;
+		ASSERT_STREQ(name.c_str(), "data_y");
+	});
+	ASSERT_EQ(x, 0);
+	ASSERT_EQ(y, 0);
+	desc_x->share(&x, true, true);
+	desc_x->reclaim();
+	ASSERT_EQ(x, 42);
+	ASSERT_EQ(y, 0);
+	desc_y->share(&y, true, true);
+	desc_y->reclaim();
+	ASSERT_EQ(x, 42);
+	ASSERT_EQ(y, 53);
+	erase_x();
+	desc_x->share(&x, true, true);
+	desc_x->reclaim();
+	ASSERT_EQ(x, 42);
+	ASSERT_EQ(y, 53);
+	desc_y->share(&y, true, true);
+	desc_y->reclaim();
+	ASSERT_EQ(x, 42);
+	ASSERT_EQ(y, 106);
+	erase_y();
+	desc_y->share(&y, true, true);
+	desc_y->reclaim();
+	ASSERT_EQ(x, 42);
+	ASSERT_EQ(y, 106);
+}
+
+/*
+ * Name:                DataDescTest.add_empty_desc_callback
+ *
+ * Tested functions:    PDI::Data_descriptor::add_empty_desc_access_callback
+ *
+ *
+ * Description:         Checks if callback is
+ *                      correctly called on empty desc access.
+ */
+TEST_F(DataDescTest, add_empty_desc_callback)
+{
+	auto desc_x = Descriptor_test_handler::default_desc(this->mockGlCtx, "data_x");
+	desc_x->default_type(Scalar_datatype{Scalar_kind::SIGNED, sizeof(int)}.clone_type());
+	desc_x->add_empty_desc_access_callback([&desc_x](const std::string& name) {
+		int* x = new int;
+		*x = 42;
+		desc_x->share(x, true, true);
+		ASSERT_STREQ(name.c_str(), "data_x");
+	});
+	Ref_r ref_read {desc_x->ref()};
+	int x = *static_cast<const int*>(ref_read.get());
+	ASSERT_EQ(x, 42);
+	int* data = static_cast<int*>(desc_x->reclaim());
+	delete data;
+}
+
+/*
+ * Name:                DataDescTest.remove_empty_desc_callback
+ *
+ * Tested functions:    PDI::Data_descriptor::add_empty_desc_access_callback
+ *
+ *
+ * Description:         Checks if callback is
+ *                      correctly called on empty desc access
+ *                      and removes it.
+ */
+TEST_F(DataDescTest, remove_empty_desc_callback)
+{
+	auto desc_x = Descriptor_test_handler::default_desc(this->mockGlCtx, "data_x");
+	desc_x->default_type(Scalar_datatype{Scalar_kind::SIGNED, sizeof(int)}.clone_type());
+	auto erase_x = desc_x->add_empty_desc_access_callback([&desc_x](const std::string& name) {
+		int* x = new int;
+		*x = 42;
+		desc_x->share(x, true, true);
+		ASSERT_STREQ(name.c_str(), "data_x");
+	});
+	Ref_r ref_read {desc_x->ref()};
+	int x = *static_cast<const int*>(ref_read.get());
+	ASSERT_EQ(x, 42);
+	int* data = static_cast<int*>(desc_x->reclaim());
+	delete data;
+	erase_x();
+	try {
+		Ref ref_x {desc_x->ref()};
+		FAIL();
+	} catch (Error e) {
+		ASSERT_EQ(e.status(), PDI_ERR_VALUE);
+	}
+}
diff --git a/tests/PDI_data_store.cxx b/tests/PDI_data_store.cxx
index 13ab681bd..3c3bcdbce 100644
--- a/tests/PDI_data_store.cxx
+++ b/tests/PDI_data_store.cxx
@@ -233,7 +233,6 @@ TEST_F(DataStoreTest, iterator)
 TEST_F(DataStoreTest, add_data_callback)
 {
 	string data_x {"data_x"};
-	EXPECT_CALL(context_mock, data()).WillOnce(testing::ReturnRef(*test_data_store));
 	this->test_data_store->desc(data_x).default_type(Scalar_datatype{Scalar_kind::SIGNED, sizeof(int)}.clone_type());
 	int x = 0;
 	this->test_data_store->add_data_callback([](const std::string& name, Ref ref) {
@@ -248,39 +247,6 @@ TEST_F(DataStoreTest, add_data_callback)
 	ASSERT_EQ(x, 42);
 }
 
-/*
- * Name:                DataStoreTest.add_named_data_callback
- *
- * Tested functions:    PDI::Data_store::add_data_callback
- *
- *
- * Description:         Checks if named callback is
- *                      correctly called on data share.
- *
- */
-TEST_F(DataStoreTest, add_named_data_callback)
-{
-	string data_x {"data_x"};
-	string data_y {"data_y"};
-	EXPECT_CALL(context_mock, data()).WillOnce(testing::ReturnRef(*test_data_store));
-	this->test_data_store->desc(data_x).default_type(Scalar_datatype{Scalar_kind::SIGNED, sizeof(int)}.clone_type());
-	this->test_data_store->desc(data_y).default_type(Scalar_datatype{Scalar_kind::SIGNED, sizeof(int)}.clone_type());
-	int x = 0;
-	int y = 0;
-	this->test_data_store->add_data_callback([](const std::string& name, Ref ref) {
-		Ref_w ref_write {ref};
-		int* x = static_cast<int*>(ref_write.get());
-		*x += 42;
-		ASSERT_STREQ(name.c_str(), "data_x");
-	}, "data_x");
-	ASSERT_EQ(x, 0);
-	ASSERT_EQ(y, 0);
-	this->test_data_store->desc("data_x").share(&x, true, true);
-	this->test_data_store->desc("data_x").reclaim();
-	ASSERT_EQ(x, 42);
-	ASSERT_EQ(y, 0);
-}
-
 /*
  * Name:                DataStoreTest.remove_data_callback
  *
@@ -294,7 +260,6 @@ TEST_F(DataStoreTest, add_named_data_callback)
 TEST_F(DataStoreTest, remove_data_callback)
 {
 	string data_x {"data_x"};
-	EXPECT_CALL(context_mock, data()).Times(2).WillRepeatedly(testing::ReturnRef(*test_data_store));
 	this->test_data_store->desc(data_x).default_type(Scalar_datatype{Scalar_kind::SIGNED, sizeof(int)}.clone_type());
 	int x = 0;
 	auto erase_x = this->test_data_store->add_data_callback([](const std::string& name, Ref ref) {
@@ -313,44 +278,6 @@ TEST_F(DataStoreTest, remove_data_callback)
 	ASSERT_EQ(x, 42);
 }
 
-/*
- * Name:                DataStoreTest.remove_named_data_callback
- *
- * Tested functions:    PDI::Data_store::add_data_callback
- *
- *
- * Description:         Checks if named callback is
- *                      correctly called on data share.
- *
- */
-TEST_F(DataStoreTest, remove_named_data_callback)
-{
-	string data_x {"data_x"};
-	string data_y {"data_y"};
-	EXPECT_CALL(context_mock, data()).Times(2).WillRepeatedly(testing::ReturnRef(*test_data_store));
-	this->test_data_store->desc(data_x).default_type(Scalar_datatype{Scalar_kind::SIGNED, sizeof(int)}.clone_type());
-	this->test_data_store->desc(data_y).default_type(Scalar_datatype{Scalar_kind::SIGNED, sizeof(int)}.clone_type());
-	int x = 0;
-	int y = 0;
-	auto erase_x = this->test_data_store->add_data_callback([](const std::string& name, Ref ref) {
-		Ref_w ref_write {ref};
-		int* x = static_cast<int*>(ref_write.get());
-		*x += 42;
-		ASSERT_STREQ(name.c_str(), "data_x");
-	}, "data_x");
-	ASSERT_EQ(x, 0);
-	ASSERT_EQ(y, 0);
-	this->test_data_store->desc("data_x").share(&x, true, true);
-	this->test_data_store->desc("data_x").reclaim();
-	ASSERT_EQ(x, 42);
-	ASSERT_EQ(y, 0);
-	erase_x();
-	this->test_data_store->desc("data_x").share(&x, true, true);
-	this->test_data_store->desc("data_x").reclaim();
-	ASSERT_EQ(x, 42);
-	ASSERT_EQ(y, 0);
-}
-
 /*
  * Name:                DataStoreTest.add_remove_data_callback
  *
@@ -384,8 +311,6 @@ TEST_F(DataStoreTest, add_remove_data_callback)
 	});
 	ASSERT_EQ(x, 0);
 	ASSERT_EQ(y, 0);
-	EXPECT_CALL(context_mock, data())
-	.Times(6).WillRepeatedly(testing::ReturnRef(*test_data_store));
 	this->test_data_store->desc("1").share(&x, true, true);
 	this->test_data_store->desc("1").reclaim();
 	ASSERT_EQ(x, 3);
@@ -412,67 +337,6 @@ TEST_F(DataStoreTest, add_remove_data_callback)
 	ASSERT_EQ(y, 10);
 }
 
-/*
- * Name:                DataStoreTest.add_remove_named_data_callback
- *
- * Tested functions:    PDI::Data_store::add_data_callback
- *
- *
- * Description:         Checks if named callback is
- *                      correctly called on share
- *                      and removes it several times.
- *
- */
-TEST_F(DataStoreTest, add_remove_named_data_callback)
-{
-	string data_x {"data_x"};
-	string data_y {"data_y"};
-	Data_descriptor& desc_x = this->test_data_store->desc(data_x);
-	Data_descriptor& desc_y = this->test_data_store->desc(data_y);
-	this->test_data_store->desc(data_x).default_type(Scalar_datatype{Scalar_kind::SIGNED, sizeof(int)}.clone_type());
-	this->test_data_store->desc(data_y).default_type(Scalar_datatype{Scalar_kind::SIGNED, sizeof(int)}.clone_type());
-	int x = 0;
-	int y = 0;
-	auto erase_x = this->test_data_store->add_data_callback([](const std::string& name, Ref ref) {
-		Ref_w ref_write {ref};
-		int* x = static_cast<int*>(ref_write.get());
-		*x += 42;
-		ASSERT_STREQ(name.c_str(), "data_x");
-	}, "data_x");
-	auto erase_y = this->test_data_store->add_data_callback([](const std::string& name, Ref ref) {
-		Ref_w ref_write {ref};
-		int* y = static_cast<int*>(ref_write.get());
-		*y += 53;
-		ASSERT_STREQ(name.c_str(), "data_y");
-	}, "data_y");
-	ASSERT_EQ(x, 0);
-	ASSERT_EQ(y, 0);
-	EXPECT_CALL(context_mock, data())
-	.Times(5).WillRepeatedly(testing::ReturnRef(*test_data_store));
-	this->test_data_store->desc("data_x").share(&x, true, true);
-	this->test_data_store->desc("data_x").reclaim();
-	ASSERT_EQ(x, 42);
-	ASSERT_EQ(y, 0);
-	this->test_data_store->desc("data_y").share(&y, true, true);
-	this->test_data_store->desc("data_y").reclaim();
-	ASSERT_EQ(x, 42);
-	ASSERT_EQ(y, 53);
-	erase_x();
-	this->test_data_store->desc("data_x").share(&x, true, true);
-	this->test_data_store->desc("data_x").reclaim();
-	ASSERT_EQ(x, 42);
-	ASSERT_EQ(y, 53);
-	this->test_data_store->desc("data_y").share(&y, true, true);
-	this->test_data_store->desc("data_y").reclaim();
-	ASSERT_EQ(x, 42);
-	ASSERT_EQ(y, 106);
-	erase_y();
-	this->test_data_store->desc("data_y").share(&y, true, true);
-	this->test_data_store->desc("data_y").reclaim();
-	ASSERT_EQ(x, 42);
-	ASSERT_EQ(y, 106);
-}
-
 /*
  * Name:                DataStoreTest.add_empty_desc_callback
  *
@@ -485,7 +349,6 @@ TEST_F(DataStoreTest, add_remove_named_data_callback)
 TEST_F(DataStoreTest, add_empty_desc_callback)
 {
 	string data_x {"data_x"};
-	EXPECT_CALL(context_mock, data()).WillRepeatedly(testing::ReturnRef(*test_data_store));
 	this->test_data_store->desc(data_x).default_type(Scalar_datatype{Scalar_kind::SIGNED, sizeof(int)}.clone_type());
 	this->test_data_store->add_empty_desc_access_callback([this](const std::string& name) {
 		int* x = new int;
@@ -512,7 +375,6 @@ TEST_F(DataStoreTest, add_empty_desc_callback)
 TEST_F(DataStoreTest, remove_empty_desc_callback)
 {
 	string data_x {"data_x"};
-	EXPECT_CALL(context_mock, data()).WillRepeatedly(testing::ReturnRef(*test_data_store));
 	this->test_data_store->desc(data_x).default_type(Scalar_datatype{Scalar_kind::SIGNED, sizeof(int)}.clone_type());
 	auto erase_x = this->test_data_store->add_empty_desc_access_callback([this](const std::string& name) {
 		int* x = new int;
diff --git a/tests/mocks/data_descriptor_mock.h b/tests/mocks/data_descriptor_mock.h
index 4ea431320..b1dd0a4f9 100644
--- a/tests/mocks/data_descriptor_mock.h
+++ b/tests/mocks/data_descriptor_mock.h
@@ -45,6 +45,9 @@ struct MockDataDescriptor : public PDI::Data_descriptor {
 	MOCK_METHOD3(share, void* (PDI::Ref, bool, bool));
 	MOCK_METHOD0(release, void());
 	MOCK_METHOD0(reclaim, void* ());
+	
+	MOCK_METHOD1(add_data_callback, std::function<void()>(const std::function<void(const std::string&, PDI::Ref)>&));
+	MOCK_METHOD1(add_empty_desc_access_callback, std::function<void()>(const std::function<void(const std::string&)>&));
 };
 
 
diff --git a/tests/mocks/data_store_mock.h b/tests/mocks/data_store_mock.h
index 4924b0e69..064c37fd6 100644
--- a/tests/mocks/data_store_mock.h
+++ b/tests/mocks/data_store_mock.h
@@ -51,10 +51,8 @@ struct MockDataStore : public PDI::Data_store {
 	MOCK_METHOD0(begin, PDI::Data_store::Iterator());
 	MOCK_METHOD0(end, PDI::Data_store::Iterator());
 	
-	MOCK_METHOD2(add_data_callback, std::function<void()>(const std::function<void(const std::string&, PDI::Ref)>&, const std::string& name));
-	MOCK_METHOD2(add_empty_desc_access_callback, std::function<void()>(const std::function<void(const std::string&)>&, const std::string& name));
-	MOCK_METHOD2(trigger_data_callbacks, void(const std::string& name, PDI::Ref));
-	MOCK_METHOD1(trigger_empty_desc_access_callbacks, void(const std::string& name));
+	MOCK_METHOD1(add_data_callback, std::function<void()>(const std::function<void(const std::string&, PDI::Ref)>&));
+	MOCK_METHOD1(add_empty_desc_access_callback, std::function<void()>(const std::function<void(const std::string&)>&));
 };
 
 
-- 
GitLab


From 1609d89020c49e8fdbb706a0f4f1dbf682fa59bf Mon Sep 17 00:00:00 2001
From: Tomasz Paluszkiewicz <tomaszp@man.poznan.pl>
Date: Wed, 10 Jul 2019 15:37:17 +0200
Subject: [PATCH 3/3] Make data_store more like a map with different iterator

---
 include/pdi/data_store.h      | 73 +++++++++++++++++++++++++++++++++--
 src/data_store.cxx            | 65 ++++++++++++++++++++++++++++++-
 src/data_store_impl.cxx       | 59 ++++++++++++++++++++++++++--
 src/data_store_impl.h         | 24 +++++++++++-
 tests/mocks/data_store_mock.h | 60 +++++++++++++++++++++++++++-
 5 files changed, 269 insertions(+), 12 deletions(-)

diff --git a/include/pdi/data_store.h b/include/pdi/data_store.h
index 97e5b59d9..138544db6 100644
--- a/include/pdi/data_store.h
+++ b/include/pdi/data_store.h
@@ -27,6 +27,7 @@
 #define PDI_DATA_STORE_H_
 
 #include <functional>
+#include <iterator>
 #include <memory>
 #include <string>
 #include <unordered_map>
@@ -44,7 +45,7 @@ class PDI_EXPORT Data_store
 public:
 	/** An iterator used to go through the descriptor store.
 	 */
-	class Iterator
+	class Iterator : public std::iterator<std::forward_iterator_tag, Data_descriptor>
 	{
 		friend class Data_store;
 		/// The iterator this wraps
@@ -55,15 +56,53 @@ public:
 		Data_descriptor* operator-> ();
 		Data_descriptor& operator* ();
 		Iterator& operator++ ();
+		Iterator operator++ (int);
+		bool operator== (const Iterator&);
 		bool operator!= (const Iterator&);
 	};
 	
+	/** A const_iterator used to go through the descriptor store.
+	 */
+	class Const_iterator : public std::iterator<std::forward_iterator_tag, Data_descriptor>
+	{
+		friend class Data_store;
+		/// The iterator this wraps
+		std::unordered_map<std::string, std::unique_ptr<Data_descriptor>>::const_iterator m_data;
+		Const_iterator(const std::unordered_map<std::string, std::unique_ptr<Data_descriptor>>::const_iterator& data);
+		Const_iterator(std::unordered_map<std::string, std::unique_ptr<Data_descriptor>>::const_iterator&& data);
+	public:
+		const Data_descriptor* operator-> ();
+		const Data_descriptor& operator* ();
+		Const_iterator& operator++ ();
+		Const_iterator operator++ (int);
+		bool operator== (const Const_iterator&);
+		bool operator!= (const Const_iterator&);
+	};
+	
 protected:
 	Iterator get_iterator(const std::unordered_map<std::string, std::unique_ptr<Data_descriptor>>::iterator& data);
 	
 	Iterator get_iterator(std::unordered_map<std::string, std::unique_ptr<Data_descriptor>>::iterator&& data);
 	
+	Const_iterator get_const_iterator(const std::unordered_map<std::string, std::unique_ptr<Data_descriptor>>::const_iterator& data) const;
+	
+	Const_iterator get_const_iterator(std::unordered_map<std::string, std::unique_ptr<Data_descriptor>>::const_iterator&& data) const;
+	
 public:
+	using key_type                  = std::string;
+	using mapped_type               = std::unique_ptr<Data_descriptor>;
+	using value_type                = std::pair<std::string, std::unique_ptr<Data_descriptor>>;
+	using size_type                 = std::size_t;
+	using hasher                    = std::hash<key_type>;
+	using key_equal                 = std::equal_to<key_type>;
+	using allocator_type            = std::allocator<std::pair<std::string, std::unique_ptr<Data_descriptor>>>;
+	using reference                 = mapped_type&;
+	using const_reference           = const mapped_type&;
+	using pointer                   = typename std::allocator_traits<allocator_type>::pointer;
+	using const_pointer             = typename std::allocator_traits<allocator_type>::const_pointer;
+	using iterator                  = Iterator;
+	using const_iterator            = Const_iterator;
+	
 	virtual ~Data_store();
 	
 	/** Accesses the descriptor for a specific name. Might be uninitialized
@@ -84,11 +123,39 @@ public:
 	
 	/** Returns an iterator on the first descriptor
 	 */
-	virtual Iterator begin() = 0;
+	virtual Iterator begin() noexcept = 0;
 	
 	/** Returns an iterator past the last descriptor
 	 */
-	virtual Iterator end() = 0;
+	virtual Iterator end() noexcept = 0;
+	
+	/** Returns a const_iterator on the first descriptor
+	 */
+	virtual Const_iterator begin() const noexcept = 0;
+	
+	/** Returns a const_iterator past the last descriptor
+	 */
+	virtual Const_iterator end() const noexcept = 0;
+	
+	/** Returns a const_iterator on the first descriptor
+	 */
+	virtual Const_iterator cbegin() const noexcept = 0;
+	
+	/** Returns a const_iterator past the last descriptor
+	 */
+	virtual Const_iterator cend() const noexcept = 0;
+	
+	virtual bool empty() const noexcept = 0;
+	
+	virtual std::size_t count(const std::string& name) const = 0;
+	
+	virtual Data_descriptor& at(const std::string& name) = 0;
+	
+	virtual Iterator find(const std::string& name) = 0;
+	
+	virtual std::size_t size() const noexcept = 0;
+	
+	virtual std::size_t max_size() const noexcept = 0;
 	
 	/** Adds new data callback to data store
 	 *
diff --git a/src/data_store.cxx b/src/data_store.cxx
index 3fa559ea8..030eda655 100644
--- a/src/data_store.cxx
+++ b/src/data_store.cxx
@@ -59,9 +59,62 @@ Data_store::Iterator& Data_store::Iterator::operator++ ()
 	return *this;
 }
 
+Data_store::Iterator Data_store::Iterator::operator++ (int)
+{
+	auto tmp = *this;
+	++(*this);
+	return tmp;
+}
+
+bool Data_store::Iterator::operator== (const Iterator& o)
+{
+	return (m_data == o.m_data);
+}
+
 bool Data_store::Iterator::operator!= (const Iterator& o)
 {
-	return (m_data != o.m_data);
+	return !(*this == o);
+}
+
+Data_store::Const_iterator::Const_iterator(const unordered_map<string, unique_ptr<Data_descriptor>>::const_iterator& data):
+	m_data(data)
+{}
+
+Data_store::Const_iterator::Const_iterator(unordered_map<string, unique_ptr<Data_descriptor>>::const_iterator&& data):
+	m_data(move(data))
+{}
+
+const Data_descriptor* Data_store::Const_iterator::operator-> ()
+{
+	return m_data->second.get();
+}
+
+const Data_descriptor& Data_store::Const_iterator::operator* ()
+{
+	return *m_data->second;
+}
+
+Data_store::Const_iterator& Data_store::Const_iterator::operator++ ()
+{
+	++m_data;
+	return *this;
+}
+
+Data_store::Const_iterator Data_store::Const_iterator::operator++ (int)
+{
+	auto tmp = *this;
+	++(*this);
+	return tmp;
+}
+
+bool Data_store::Const_iterator::operator== (const Const_iterator& o)
+{
+	return (m_data == o.m_data);
+}
+
+bool Data_store::Const_iterator::operator!= (const Const_iterator& o)
+{
+	return !(*this == o);
 }
 
 Data_store::Iterator Data_store::get_iterator(const std::unordered_map<std::string, unique_ptr<Data_descriptor>>::iterator& data)
@@ -74,6 +127,16 @@ Data_store::Iterator Data_store::get_iterator(std::unordered_map<std::string, un
 	return move(data);
 }
 
+Data_store::Const_iterator Data_store::get_const_iterator(const std::unordered_map<std::string, unique_ptr<Data_descriptor>>::const_iterator& data) const
+{
+	return data;
+}
+
+Data_store::Const_iterator Data_store::get_const_iterator(std::unordered_map<std::string, unique_ptr<Data_descriptor>>::const_iterator&& data) const
+{
+	return move(data);
+}
+
 Data_store::~Data_store() = default;
 
 }
diff --git a/src/data_store_impl.cxx b/src/data_store_impl.cxx
index dca49507a..fa821f7a7 100644
--- a/src/data_store_impl.cxx
+++ b/src/data_store_impl.cxx
@@ -43,6 +43,7 @@ using std::exception;
 using std::function;
 using std::move;
 using std::reference_wrapper;
+using std::size_t;
 using std::string;
 using std::to_string;
 using std::unordered_map;
@@ -76,17 +77,67 @@ Data_descriptor& Data_store_impl::operator[](const string& name)
 	return desc(name.c_str());
 }
 
-Data_store::Iterator Data_store_impl::begin()
+Data_store::Iterator Data_store_impl::begin() noexcept
 {
 	return Data_store::get_iterator(m_descriptors.begin());
 }
 
-Data_store::Iterator Data_store_impl::end()
+Data_store::Iterator Data_store_impl::end() noexcept
 {
 	return Data_store::get_iterator(m_descriptors.end());
 }
 
-std::function<void()> Data_store_impl::add_data_callback(const std::function<void(const std::string&, Ref)>& callback)
+Data_store::Const_iterator Data_store_impl::begin() const noexcept
+{
+	return cbegin();
+}
+
+Data_store::Const_iterator Data_store_impl::end() const noexcept
+{
+	return cend();
+}
+
+Data_store::Const_iterator Data_store_impl::cbegin() const noexcept
+{
+	return Data_store::get_const_iterator(m_descriptors.cbegin());
+}
+
+Data_store::Const_iterator Data_store_impl::cend() const noexcept
+{
+	return Data_store::get_const_iterator(m_descriptors.cend());
+}
+
+bool Data_store_impl::empty() const noexcept
+{
+	return m_descriptors.empty();
+}
+
+std::size_t Data_store_impl::count(const std::string& name) const
+{
+	return m_descriptors.empty();
+}
+
+Data_descriptor& Data_store_impl::at(const std::string& name)
+{
+	return *m_descriptors.at(name);
+}
+
+Data_store::Iterator Data_store_impl::find(const std::string& name)
+{
+	return Data_store::get_iterator(m_descriptors.find(name));
+}
+
+size_t Data_store_impl::size() const noexcept
+{
+	return m_descriptors.size();
+}
+
+size_t Data_store_impl::max_size() const noexcept
+{
+	return m_descriptors.max_size();
+}
+
+function<void()> Data_store_impl::add_data_callback(const function<void(const string&, Ref)>& callback)
 {
 	m_data_callbacks.emplace_back(callback);
 	auto it = --m_data_callbacks.end();
@@ -95,7 +146,7 @@ std::function<void()> Data_store_impl::add_data_callback(const std::function<voi
 	};
 }
 
-std::function<void()> Data_store_impl::add_empty_desc_access_callback(const std::function<void(const std::string&)>& callback)
+function<void()> Data_store_impl::add_empty_desc_access_callback(const function<void(const string&)>& callback)
 {
 	m_empty_desc_access_callbacks.emplace_back(callback);
 	auto it = --m_empty_desc_access_callbacks.end();
diff --git a/src/data_store_impl.h b/src/data_store_impl.h
index a21b472ce..107589e68 100644
--- a/src/data_store_impl.h
+++ b/src/data_store_impl.h
@@ -73,9 +73,29 @@ public:
 
 	Data_descriptor& operator[](const char* name) override;
 
-	Iterator begin() override;
+	Iterator begin() noexcept override;
 
-	Iterator end() override;
+	Iterator end() noexcept override;
+
+	Const_iterator begin() const noexcept override;
+
+	Const_iterator end() const noexcept override;
+
+	Const_iterator cbegin() const noexcept override;
+	
+	Const_iterator cend() const noexcept override;
+
+	bool empty() const noexcept override;
+
+	std::size_t count(const std::string& name) const override;
+
+	Data_descriptor& at(const std::string& name) override;
+
+	Iterator find(const std::string& name) override;
+
+	std::size_t size() const noexcept override;
+
+	std::size_t max_size() const noexcept override;
 
 	std::function<void()> add_data_callback(const std::function<void(const std::string&, Ref)>& callback) override;
 	
diff --git a/tests/mocks/data_store_mock.h b/tests/mocks/data_store_mock.h
index 064c37fd6..9da451c79 100644
--- a/tests/mocks/data_store_mock.h
+++ b/tests/mocks/data_store_mock.h
@@ -48,8 +48,64 @@ struct MockDataStore : public PDI::Data_store {
 		return BracketOp2(str);
 	}
 	
-	MOCK_METHOD0(begin, PDI::Data_store::Iterator());
-	MOCK_METHOD0(end, PDI::Data_store::Iterator());
+	//each noexcept method must be used through proxy (no support from gmock)
+	MOCK_METHOD0(begin_proxy, PDI::Data_store::Iterator());
+	PDI::Data_store::Iterator begin() noexcept override
+	{
+		return begin_proxy();
+	}
+	
+	MOCK_METHOD0(end_proxy, PDI::Data_store::Iterator());
+	PDI::Data_store::Iterator end() noexcept override
+	{
+		return end_proxy();
+	}
+	
+	MOCK_CONST_METHOD0(begin_const_proxy, PDI::Data_store::Const_iterator());
+	PDI::Data_store::Const_iterator begin() const noexcept override
+	{
+		return begin_const_proxy();
+	}
+	
+	MOCK_CONST_METHOD0(end_const_proxy, PDI::Data_store::Const_iterator());
+	PDI::Data_store::Const_iterator end() const noexcept override
+	{
+		return end_const_proxy();
+	}
+	
+	MOCK_CONST_METHOD0(cbegin_proxy, PDI::Data_store::Const_iterator());
+	PDI::Data_store::Const_iterator cbegin() const noexcept override
+	{
+		return cbegin_proxy();
+	}
+	
+	MOCK_CONST_METHOD0(cend_proxy, PDI::Data_store::Const_iterator());
+	PDI::Data_store::Const_iterator cend() const noexcept override
+	{
+		return cend_proxy();
+	}
+	
+	MOCK_CONST_METHOD0(empty_proxy, bool());
+	bool empty() const noexcept override
+	{
+		return empty_proxy();
+	}
+	
+	MOCK_CONST_METHOD0(size_proxy, std::size_t());
+	std::size_t size() const noexcept override
+	{
+		return size_proxy();
+	}
+	
+	MOCK_CONST_METHOD0(max_size_proxy, std::size_t());
+	std::size_t max_size() const noexcept override
+	{
+		return max_size_proxy();
+	}
+	
+	MOCK_CONST_METHOD1(count, std::size_t(const std::string&));
+	MOCK_METHOD1(at, PDI::Data_descriptor&(const std::string&));
+	MOCK_METHOD1(find, PDI::Data_store::Iterator(const std::string&));
 	
 	MOCK_METHOD1(add_data_callback, std::function<void()>(const std::function<void(const std::string&, PDI::Ref)>&));
 	MOCK_METHOD1(add_empty_desc_access_callback, std::function<void()>(const std::function<void(const std::string&)>&));
-- 
GitLab