[med-svn] [odil] 01/08: Imported Upstream version 0.8.0

Julien Lamy lamy-guest at moszumanska.debian.org
Thu Apr 20 09:52:37 UTC 2017


This is an automated email from the git hooks/post-receive script.

lamy-guest pushed a commit to branch master
in repository odil.

commit 78d9019fab7e22a21c2fd22a4f9475acc43616ac
Author: root <root at 0081a9e9b04b>
Date:   Wed Apr 19 10:31:24 2017 +0000

    Imported Upstream version 0.8.0
---
 .travis.yml                                      |  18 +-
 CMakeLists.txt                                   |  22 +-
 FindICU.cmake                                    |  15 +-
 FindLog4Cpp.cmake                                |   2 +-
 README.md                                        |  11 +-
 applications/odil                                |   3 +-
 applications/store.py                            |  68 ++++
 appveyor.full.yml                                |  52 ---
 appveyor.yml                                     | 113 ++++--
 ctest-to-junit.xsl                               |  64 ++++
 examples/CMakeLists.txt                          |   6 +-
 examples/find.cpp                                |  10 +-
 examples/genericscp.cpp                          |  22 +-
 src/CMakeLists.txt                               |  10 +-
 src/odil/Association.cpp                         |   8 +-
 src/odil/Association.h                           |   9 +-
 src/odil/AssociationAcceptor.h                   |   4 +-
 src/odil/AssociationParameters.cpp               |   8 +-
 src/odil/AssociationParameters.h                 |   7 +-
 src/odil/BasicDirectoryCreator.h                 |   3 +-
 src/odil/DataSet.cpp                             | 215 +++++------
 src/odil/DataSet.h                               |  78 ++--
 src/odil/EchoSCP.cpp                             |  14 +
 src/odil/EchoSCP.h                               |   7 +-
 src/odil/EchoSCU.h                               |   3 +-
 src/odil/Element.cpp                             | 137 ++++---
 src/odil/Element.h                               |  88 ++---
 src/odil/ElementsDictionary.h                    |  76 ++--
 src/odil/Exception.h                             |   4 +-
 src/odil/FindSCP.cpp                             |  18 +-
 src/odil/FindSCP.h                               |   9 +-
 src/odil/FindSCU.cpp                             |  77 +++-
 src/odil/FindSCU.h                               |  18 +-
 src/odil/GetSCP.cpp                              |  18 +-
 src/odil/GetSCP.h                                |   8 +-
 src/odil/GetSCU.cpp                              |  96 +++--
 src/odil/GetSCU.h                                |  25 +-
 src/odil/MoveSCP.cpp                             |  20 +-
 src/odil/MoveSCP.h                               |   7 +-
 src/odil/MoveSCU.cpp                             |  72 +++-
 src/odil/MoveSCU.h                               |  28 +-
 src/odil/{StoreSCP.cpp => NCreateSCP.cpp}        |  53 +--
 src/odil/{EchoSCP.h => NCreateSCP.h}             |  27 +-
 src/odil/NSetSCP.cpp                             | 102 ++++++
 src/odil/{EchoSCP.h => NSetSCP.h}                |  28 +-
 src/odil/NSetSCU.cpp                             |  74 ++++
 src/odil/{StoreSCU.h => NSetSCU.h}               |  20 +-
 src/odil/Reader.cpp                              |  46 +--
 src/odil/Reader.h                                |   3 +-
 src/odil/SCP.cpp                                 |   4 +-
 src/odil/SCP.h                                   |   8 +-
 src/odil/SCPDispatcher.cpp                       |  27 +-
 src/odil/SCPDispatcher.h                         |   7 +-
 src/odil/SCU.h                                   |   3 +-
 src/odil/StoreSCP.cpp                            |  18 +-
 src/odil/StoreSCP.h                              |   9 +-
 src/odil/StoreSCU.cpp                            |  37 +-
 src/odil/StoreSCU.h                              |  12 +-
 src/odil/Tag.h                                   |  16 +-
 src/odil/UIDsDictionary.h                        |   4 +-
 src/odil/VR.h                                    |  16 +-
 src/odil/VRFinder.h                              |   3 +-
 src/odil/Value.cpp                               | 164 ++++++---
 src/odil/Value.h                                 |  69 ++--
 src/odil/Value.txx                               |  12 +-
 src/odil/Writer.h                                |   3 +-
 src/odil/base64.h                                |   6 +-
 src/odil/dcmtk/ElementAccessor.h                 |   5 +-
 src/odil/dcmtk/Exception.h                       |   3 +-
 src/odil/dcmtk/conversion.cpp                    |  48 +--
 src/odil/dcmtk/conversion.h                      |  20 +-
 src/odil/dul/EventData.h                         |   3 +-
 src/odil/dul/StateMachine.h                      |   3 +-
 src/odil/dul/Transport.h                         |   4 +-
 src/odil/json_converter.cpp                      |  12 +-
 src/odil/json_converter.h                        |   5 +-
 src/odil/message/CEchoRequest.h                  |   3 +-
 src/odil/message/CEchoResponse.h                 |   3 +-
 src/odil/message/CFindRequest.cpp                |  54 ++-
 src/odil/message/CFindRequest.h                  |  24 +-
 src/odil/message/CFindResponse.cpp               |  47 ++-
 src/odil/message/CFindResponse.h                 |  21 +-
 src/odil/message/CGetRequest.cpp                 |  54 ++-
 src/odil/message/CGetRequest.h                   |  24 +-
 src/odil/message/CGetResponse.cpp                |  51 ++-
 src/odil/message/CGetResponse.h                  |  21 +-
 src/odil/message/CMoveRequest.cpp                |  56 ++-
 src/odil/message/CMoveRequest.h                  |  26 +-
 src/odil/message/CMoveResponse.cpp               |  51 ++-
 src/odil/message/CMoveResponse.h                 |  21 +-
 src/odil/message/CStoreRequest.cpp               |  64 +++-
 src/odil/message/CStoreRequest.h                 |  31 +-
 src/odil/message/CStoreResponse.h                |   3 +-
 src/odil/message/Cancellation.h                  |   3 +-
 src/odil/message/Message.cpp                     |  41 +++
 src/odil/message/Message.h                       |  18 +-
 src/odil/message/NCreateRequest.cpp              |  51 +++
 src/odil/message/NCreateRequest.h                |  63 ++++
 src/odil/message/NCreateResponse.cpp             |  53 +++
 src/odil/message/NCreateResponse.h               |  61 ++++
 src/odil/message/NSetRequest.cpp                 |  93 +++++
 src/odil/message/NSetRequest.h                   |  59 +++
 src/odil/message/NSetResponse.cpp                |  61 ++++
 src/odil/message/NSetResponse.h                  |  58 +++
 src/odil/message/Request.h                       |   3 +-
 src/odil/message/Response.h                      |   3 +-
 src/odil/odil.h                                  |   7 +-
 src/odil/pdu/AAbort.h                            |   3 +-
 src/odil/pdu/AAssociate.h                        |   3 +-
 src/odil/pdu/AAssociateAC.h                      |   3 +-
 src/odil/pdu/AAssociateRJ.h                      |   3 +-
 src/odil/pdu/AAssociateRQ.h                      |   3 +-
 src/odil/pdu/AReleaseRP.h                        |   3 +-
 src/odil/pdu/AReleaseRQ.h                        |   3 +-
 src/odil/pdu/ApplicationContext.h                |   3 +-
 src/odil/pdu/AsynchronousOperationsWindow.h      |   3 +-
 src/odil/pdu/ImplementationClassUID.h            |   3 +-
 src/odil/pdu/ImplementationVersionName.h         |   3 +-
 src/odil/pdu/Item.h                              |   7 +-
 src/odil/pdu/MaximumLength.h                     |   3 +-
 src/odil/pdu/Object.h                            |   5 +-
 src/odil/pdu/PDataTF.h                           |   5 +-
 src/odil/pdu/PresentationContext.h               |   3 +-
 src/odil/pdu/PresentationContextAC.h             |   3 +-
 src/odil/pdu/PresentationContextRQ.h             |   3 +-
 src/odil/pdu/RoleSelection.h                     |   3 +-
 src/odil/pdu/SOPClassCommonExtendedNegotiation.h |   3 +-
 src/odil/pdu/SOPClassExtendedNegotiation.h       |   3 +-
 src/odil/pdu/UserIdentityAC.h                    |   3 +-
 src/odil/pdu/UserIdentityRQ.h                    |   3 +-
 src/odil/pdu/UserInformation.h                   |   3 +-
 src/odil/registry.cpp                            |  59 ++-
 src/odil/registry.h                              |  19 +-
 src/odil/uid.h                                   |   2 +-
 src/odil/unicode.h                               |   6 +-
 src/odil/write_ds.h                              |   4 +-
 src/odil/xml_converter.cpp                       |  24 +-
 src/odil/xml_converter.h                         |   5 +-
 tests/CMakeLists.txt                             |   7 +-
 tests/code/BasicDirectoryCreator.cpp             |  15 +-
 tests/code/DataSet.cpp                           | 445 +++++++++++------------
 tests/code/Element.cpp                           | 353 ++++++++++++------
 tests/code/FindSCP.cpp                           |  18 +-
 tests/code/FindSCU.cpp                           |  27 ++
 tests/code/GetSCP.cpp                            |  20 +-
 tests/code/GetSCU.cpp                            |  40 ++
 tests/code/MoveSCP.cpp                           |  18 +-
 tests/code/MoveSCU.cpp                           |  48 +++
 tests/code/NCreateSCP.cpp                        |  91 +++++
 tests/code/Reader.cpp                            |   2 +-
 tests/code/SCPDispatcher.cpp                     |   2 +-
 tests/code/StoreSCU.cpp                          |   8 +
 tests/code/Value.cpp                             | 379 ++++++++-----------
 tests/run                                        |  24 +-
 tests/tools/CMakeLists.txt                       |   8 +-
 tests/wrappers/test_association_parameters.py    |   4 +-
 tests/wrappers/test_data_set.py                  | 243 ++++++-------
 tests/wrappers/test_element.py                   | 194 +++++++---
 tests/wrappers/test_tag.py                       |  11 +
 tests/wrappers/test_value.py                     | 150 ++++++--
 tests/wrappers/test_vr.py                        |  20 +
 wrappers/Assocation.cpp                          | 110 +++---
 wrappers/AssocationParameters.cpp                |   3 +-
 wrappers/CEchoResponse.cpp                       |  30 ++
 wrappers/CMakeLists.txt                          |  20 +
 wrappers/CStoreResponse.cpp                      |  54 +++
 wrappers/DataSet.cpp                             |   2 +
 wrappers/EchoSCP.cpp                             |  32 +-
 wrappers/Element.cpp                             |  12 +-
 wrappers/FindSCP.cpp                             |   7 +-
 wrappers/GetSCP.cpp                              |   7 +-
 wrappers/Message.cpp                             |  65 +++-
 wrappers/MoveSCP.cpp                             |   7 +-
 wrappers/{EchoSCP.cpp => NCreateSCP.cpp}         |  17 +-
 wrappers/NSetRequest.cpp                         |  50 +++
 wrappers/{EchoSCP.cpp => NSetSCP.cpp}            |  27 +-
 wrappers/{StoreSCU.cpp => NSetSCU.cpp}           |  16 +-
 wrappers/Response.cpp                            |  34 ++
 wrappers/{EchoSCP.cpp => SCP.cpp}                |  15 +-
 wrappers/SCPDispatcher.cpp                       |  47 +++
 wrappers/StoreSCP.cpp                            |  31 +-
 wrappers/StoreSCU.cpp                            |  14 +-
 wrappers/Tag.cpp                                 |   6 +
 wrappers/VR.cpp                                  |  70 ++++
 wrappers/Value.cpp                               |   6 +-
 wrappers/odil.cpp                                |  19 +-
 wrappers/value_constructor.cpp                   |  41 ++-
 187 files changed, 4644 insertions(+), 1899 deletions(-)

diff --git a/.travis.yml b/.travis.yml
index 1c6a9ab..4c029d9 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -5,8 +5,8 @@ matrix:
       sudo: required
       dist: trusty
       compiler: gcc
-#    - os: osx
-#      compiler: clang
+    - os: osx
+      compiler: clang
 addons:
   apt:
     packages:
@@ -16,6 +16,7 @@ addons:
     - libicu-dev
     - zlib1g-dev
     - libboost-dev
+    - libboost-date-time-dev
     - libboost-filesystem-dev
     - libboost-python-dev
     - libboost-regex-dev
@@ -27,7 +28,7 @@ addons:
 before_install:
   - if [ "$TRAVIS_OS_NAME" = "osx" ]; then brew update; fi
   # JSONCpp conflicts with json-c
-  - if [ "$TRAVIS_OS_NAME" = "osx" ]; then brew uninstall json-c; fi
+  - if [ "$TRAVIS_OS_NAME" = "osx" ]; then brew uninstall --ignore-dependencies json-c; fi
   # Boost is already installed with another version
   - if [ "$TRAVIS_OS_NAME" = "osx" ]; then brew unlink boost; brew install boost boost-python; fi
   - if [ "$TRAVIS_OS_NAME" = "osx" ]; then brew install dcmtk icu4c jsoncpp log4cpp; fi
@@ -38,12 +39,15 @@ before_script:
   - mkdir build
   - cd build
   - export BIN_DIR=$PWD
-  - CMAKE_CXX_FLAGS="-std=c++11"
-  - if [ "${CC}" = "gcc" ]; then CMAKE_CXX_FLAGS="${CMAKE_CXX_FLAGS} --coverage"; fi
+  - CMAKE_OPTIONS=""
+  - if [ "${CC}" = "gcc" ]; then CMAKE_OPTIONS='${CMAKE_OPTIONS} -DCMAKE_CXX_FLAGS="--coverage"'; fi
+  #- if [ "${CC}" = "gcc" ]; then CMAKE_OPTIONS='${CMAKE_OPTIONS} -DPYTHON_LIBRARY=/opt/python/2.7.12/lib/libpython2.7.so'; fi
+  #- if [ "$TRAVIS_OS_NAME" = "osx" ]; then CMAKE_OPTIONS='${CMAKE_OPTIONS} -DPYTHON_LIBRARY=/usr/local/Frameworks/Python.framework/Versions/2.7/lib/libpython2.7.dylib' ; fi
   - if [ "$TRAVIS_OS_NAME" = "osx" ]; then export PKG_CONFIG_PATH=/usr/local/lib/pkgconfig:/usr/local/opt/icu4c/lib/pkgconfig; fi
-  - cmake -D CMAKE_CXX_FLAGS:STRING="${CMAKE_CXX_FLAGS}" -D CMAKE_BUILD_TYPE:STRING=Debug ../
+  # Travis has a weird Python environment. Disable the wrappers
+  - cmake ${CMAKE_OPTIONS} -DBUILD_WRAPPERS=OFF ../
 script:
   - make
-  - ../tests/run --no-network
+  - ../tests/run --no-network -e ".*"
 after_success:
   - if [ "${CC}" = "gcc" ]; then coveralls --exclude examples --exclude tests --exclude-pattern '.*CMake[^/]+\.c(?:pp)?' --exclude-pattern "/usr/.*" --root=${SRC_DIR} --build-root ${BIN_DIR} > /dev/null; fi
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 9c34df5..ce37237 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -2,8 +2,8 @@ cmake_minimum_required(VERSION 2.8)
 
 project("odil")
 set(odil_MAJOR_VERSION 0)
-set(odil_MINOR_VERSION 7)
-set(odil_PATCH_VERSION 2)
+set(odil_MINOR_VERSION 8)
+set(odil_PATCH_VERSION 0)
 set(odil_VERSION
     ${odil_MAJOR_VERSION}.${odil_MINOR_VERSION}.${odil_PATCH_VERSION})
 
@@ -12,19 +12,27 @@ option(BUILD_EXAMPLES "Build the examples directory." ON)
 option(BUILD_WRAPPERS "Build the Python Wrappers." ON)
 
 option(WITH_DCMTK "Build the DCMTK converter" ON)
+option(
+    USE_BUILTIN_DCMTK_GETSCU
+    "Compile a locally packaged version of getscu for old DCMTK versions" ON)
 
 set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}" ${CMAKE_MODULE_PATH})
 include(CTest)
 include(cmake/functions.cmake)
 
+# Add the C++0x or C++11 flag
+include(CheckCXXCompilerFlag)
+CHECK_CXX_COMPILER_FLAG(-std=c++11 COMPILER_SUPPORTS_CXX11)
+CHECK_CXX_COMPILER_FLAG(-std=c++0x COMPILER_SUPPORTS_CXX0X)
+if(COMPILER_SUPPORTS_CXX11)
+    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
+elseif(COMPILER_SUPPORTS_CXX0X)
+    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x")
+endif()
+
 set_property(GLOBAL PROPERTY USE_FOLDERS ON)
 
 if(WIN32)
-    # Trying the automatic creation of .def files by CMake
-    if(${BUILD_SHARED_LIBS})
-        set(WINDOWS_EXPORT_ALL_SYMBOLS TRUE)
-    endif()
-
     # We have to set _WIN32_WINNT for Asio
     if(${CMAKE_SYSTEM_VERSION} EQUAL 10) # Windows 10
         add_definitions(-D _WIN32_WINNT=0x0A00)
diff --git a/FindICU.cmake b/FindICU.cmake
index 61e9d72..f9ce7e2 100644
--- a/FindICU.cmake
+++ b/FindICU.cmake
@@ -10,7 +10,20 @@ pkg_check_modules(PC_ICU QUIET icu-uc)
 set(ICU_DEFINITIONS ${PC_ICU_CFLAGS_OTHER})
 
 find_path(ICU_INCLUDE_DIR "unicode/ucnv.h" HINTS ${PC_ICU_INCLUDE_DIRS})
-find_library(ICU_LIBRARY NAMES icuuc HINTS ${PC_ICU_LIBRARY_DIRS} )
+
+# Get version
+if(ICU_INCLUDE_DIR AND EXISTS "${ICU_INCLUDE_DIR}/unicode/uvernum.h")
+	file(STRINGS "${ICU_INCLUDE_DIR}/unicode/uvernum.h" icu_header_str
+	  REGEX "^#define[\t ]+U_ICU_VERSION[\t ]+\".*\".*")
+
+	string(REGEX REPLACE "^#define[\t ]+U_ICU_VERSION[\t ]+\"([0-9]*).*"
+	  "\\1" icu_version_string "${icu_header_str}")
+	set(ICU_VERSION "${icu_version_string}" )
+	unset(icu_header_str)
+	unset(icu_version_string)
+endif()
+
+find_library(ICU_LIBRARY NAMES icuuc icuuc${ICU_VERSION} icuuc${ICU_VERSION}d HINTS ${PC_ICU_LIBRARY_DIRS} )
 
 set(ICU_LIBRARIES ${ICU_LIBRARY} )
 set(ICU_INCLUDE_DIRS ${ICU_INCLUDE_DIR} )
diff --git a/FindLog4Cpp.cmake b/FindLog4Cpp.cmake
index 7f83da4..00c867c 100644
--- a/FindLog4Cpp.cmake
+++ b/FindLog4Cpp.cmake
@@ -10,7 +10,7 @@ pkg_check_modules(PC_Log4Cpp QUIET log4cpp)
 set(Log4Cpp_DEFINITIONS ${PC_Log4Cpp_CFLAGS_OTHER})
 
 find_path(Log4Cpp_INCLUDE_DIR "log4cpp/Category.hh" HINTS ${PC_Log4Cpp_INCLUDE_DIRS})
-find_library(Log4Cpp_LIBRARY NAMES log4cpp HINTS ${PC_Log4Cpp_LIBRARY_DIRS} )
+find_library(Log4Cpp_LIBRARY NAMES log4cpp log4cppD HINTS ${PC_Log4Cpp_LIBRARY_DIRS} )
 
 set(Log4Cpp_LIBRARIES ${Log4Cpp_LIBRARY} )
 set(Log4Cpp_INCLUDE_DIRS ${Log4Cpp_INCLUDE_DIR} )
diff --git a/README.md b/README.md
index 7de4d0a..7a1435a 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,4 @@
-Odil
-=======
+# Odil
 
 Odil is a C++11 library for the [DICOM](http://dicom.nema.org/) standard.
 
@@ -13,11 +12,13 @@ Odil also provides conversion to and from
 [DCMTK](http://dicom.offis.de/dcmtk.php.en) data structures.
 
 Odil builds and run on:
+
 * Linux (Debian 7 and 8, Ubuntu 12.04, 14.04, and 16.04, all 32 and 64 bits).
   Official packages are available ([Debian](https://packages.debian.org/search?keywords=odil&searchon=sourcenames&suite=all&section=all), [Ubuntu](http://packages.ubuntu.com/search?keywords=odil&searchon=sourcenames&suite=all&section=all)),
-  as well as [unofficial backports](https://github.com/lamyj/packages).
-    
+  as well as [unofficial backports](https://github.com/lamyj/packages).    
 * OS X
+* Windows
 
-[![Build Status](https://travis-ci.org/lamyj/odil.svg?branch=master)](https://travis-ci.org/lamyj/odil)
+[![Build Status (Travis)](https://travis-ci.org/lamyj/odil.svg?branch=master)](https://travis-ci.org/lamyj/odil)
+[![Build Status (Appveyor)](https://ci.appveyor.com/api/projects/status/github/lamyj/odil?svg=true)](https://ci.appveyor.com/project/lamyj/odil)
 [![Coverage Status](https://coveralls.io/repos/lamyj/odil/badge.svg)](https://coveralls.io/r/lamyj/odil)
diff --git a/applications/odil b/applications/odil
index 97e4d6f..07c3cc5 100755
--- a/applications/odil
+++ b/applications/odil
@@ -9,6 +9,7 @@ import echo
 import find
 import get
 import print_
+import store
 import transcode
 
 def main():
@@ -18,7 +19,7 @@ def main():
 
     modules = [
         print_, transcode, dicomdir, 
-        echo, find, get
+        echo, find, get, store
     ]
     for module in modules:
         sub_parser = module.add_subparser(subparsers)
diff --git a/applications/store.py b/applications/store.py
new file mode 100755
index 0000000..cd628f2
--- /dev/null
+++ b/applications/store.py
@@ -0,0 +1,68 @@
+from __future__ import print_function
+
+import argparse
+
+import odil
+
+def add_subparser(subparsers):
+    parser = subparsers.add_parser(
+        "store", help="DICOM store (C-STORE)",
+        formatter_class=argparse.ArgumentDefaultsHelpFormatter)
+    parser.add_argument("host", help="Remote host address")
+    parser.add_argument("port", type=int, help="Remote host port")
+    parser.add_argument(
+        "calling_ae_title", help="AE title of the calling application")
+    parser.add_argument(
+        "called_ae_title", help="AE title of the called application")
+    parser.add_argument("filenames", nargs="+", help="File names")
+    parser.set_defaults(function=store)
+    return parser
+
+def store(host, port, calling_ae_title, called_ae_title, filenames):
+    transfer_syntaxes = [
+        odil.registry.ExplicitVRLittleEndian,
+        odil.registry.ImplicitVRLittleEndian,
+    ]
+    
+    # Find all SOP classes to negotiate at association time. We don't need to
+    # read the whole data set for this
+    sop_classes = set()
+    for filename in filenames:
+        _, data_set = odil.read(
+            filename, halt_condition=lambda tag: tag>odil.registry.SOPClassUID)
+        sop_classes.update(data_set.as_string("SOPClassUID"))
+
+    print(sop_classes)
+    presentation_contexts = [
+        odil.AssociationParameters.PresentationContext(
+            2*i+1, sop_class, transfer_syntaxes, True, False)
+        for i, sop_class in enumerate(sop_classes)
+    ]
+    
+    # Create the association and the Store SCU
+    association = odil.Association()
+    association.set_peer_host(host)
+    association.set_peer_port(port)
+    association.update_parameters()\
+        .set_calling_ae_title(calling_ae_title)\
+        .set_called_ae_title(called_ae_title)\
+        .set_presentation_contexts(presentation_contexts)
+    association.associate()
+    
+    negotiated_parameters = association.get_negotiated_parameters()
+    negotiated_pc = negotiated_parameters.get_presentation_contexts()
+    for pc in negotiated_pc:
+        print(pc.abstract_syntax, " ", pc.transfer_syntaxes[0])
+    
+    store = odil.StoreSCU(association)
+    
+    for filename in filenames:
+        _, data_set = odil.read(filename)
+        
+        try:
+            store.set_affected_sop_class(data_set)
+            store.store(data_set)
+        except Exception as e:
+            print("Could not store {}: {}".format(filename, e))
+    
+    association.release()
diff --git a/appveyor.full.yml b/appveyor.full.yml
deleted file mode 100644
index 092d607..0000000
--- a/appveyor.full.yml
+++ /dev/null
@@ -1,52 +0,0 @@
-version: "{build}"
-
-os: Visual Studio 2015
-
-clone_folder: c:\projects\dcmtkpp
-
-environment:
-  BOOST_ROOT: C:/Libraries/boost_1_59_0
-  BOOST_LIBRARYDIR: C:/Libraries/boost_1_59_0/lib64-msvc-14.0
-  ICU_INCLUDE_DIR: C:/Libraries/icu/include
-  ICU_LIBRARY: C:/Libraries/icu/lib64/icuuc.lib
-  JsonCpp_INCLUDE_DIR: c:/Libraries/jsoncpp_0_10_5/include
-  JsonCpp_LIBRARY: c:/Libraries/jsoncpp_0_10_5/lib/jsoncpp.lib
-  DCMTK_INCLUDE_DIR: C:/Libraries/dcmtk-3.6.1_20150924/include
-  DCMTK_LIBRARY: C:/Libraries/dcmtk-3.6.1_20150924/lib/dcmdata.lib
-
-#init:
-#- ps: iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1'))
-
-install:
-  # ICU4C
-- ps: Start-FileDownload http://download.icu-project.org/files/icu4c/56.1/icu4c-56_1-Win64-msvc10.zip
-- 7z x -oC:\Libraries icu4c-56_1-Win64-msvc10.zip
-  # JsonCpp
-- ps: Start-FileDownload https://github.com/open-source-parsers/jsoncpp/archive/0.10.5.zip
-- 7z x -oC:\projects 0.10.5.zip
-- cd C:\projects\jsoncpp-0.10.5
-- mkdir build
-- cd build
-- cmake -D CMAKE_INSTALL_PREFIX=c:\Libraries\jsoncpp_0_10_5 ..
-- cmake --build . --config release --target install
-  # DCMTK
-- ps: Start-FileDownload http://dicom.offis.de/download/dcmtk/snapshot/dcmtk-3.6.1_20150924.tar.gz
-- 7z x -so dcmtk-3.6.1_20150924.tar.gz | 7z x -si -oC:\projects -ttar
-- cd C:\projects\dcmtk-3.6.1_20150924
-- mkdir build
-- cd build
-- cmake -D CMAKE_INSTALL_PREFIX=c:\Libraries\dcmtk-3.6.1_20150924 ..
-- cmake --build . --config release --target install
-
-before_build:
-  - cd c:\projects\dcmtkpp
-  - md build
-  - cd build
-  - set PATH=C:\Program Files (x86)\MSBuild\14.0\Bin;%PATH%
-  - cmake -DBOOST_ROOT="%BOOST_ROOT%" -DBOOST_LIBRARYDIR="%BOOST_LIBRARYDIR%" -DICU_INCLUDE_DIR="%ICU_INCLUDE_DIR%" -DICU_LIBRARY="%ICU_LIBRARY%" -DJsonCpp_INCLUDE_DIR="%JsonCpp_INCLUDE_DIR%" -DJsonCpp_LIBRARY="%JsonCpp_LIBRARY%" -DDCMTK_INCLUDE_DIR="%DCMTK_INCLUDE_DIR%" -DDCMTK_LIBRARY="%DCMTK_LIBRARY%" ..
-
-build:
-  project: C:\projects\dcmtkpp\build\dcmtkpp.sln
-
-#on_finish:
-#- ps: $blockRdp = $true; iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1'))
diff --git a/appveyor.yml b/appveyor.yml
index edbf936..923006b 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -1,43 +1,86 @@
 version: "{build}"
+image: Visual Studio 2015
+platform:
+  - x64
+configuration:
+  - Release
 
-os: Windows Server 2012 R2
+environment:
+  BOOST_ROOT: C:/Libraries/boost_1_62_0
+  DCMTK_ROOT: c:/Libraries/dcmtk
+  ICU_ROOT: C:/Libraries/icu
+  JsonCpp_ROOT: C:/Libraries/jsoncpp
+  Log4Cpp_ROOT: C:/Libraries/log4cpp
+  Python_ROOT: C:/Python27-x64
+  VERSION: 0.8.0-1
 
-clone_folder: c:\projects\odil
+install:
+  - ps: |
+      Start-FileDownload https://github.com/lamyj/appveyor-dcmtk/releases/download/v3.6.1-20161102-2/dcmtk_3.6.1-20161102-2_dynamic_x64.zip dcmtk.zip
+      7z x -bd -oC:\Libraries dcmtk.zip
 
-environment:
-  BOOST_ROOT: C:/Libraries/boost_1_59_0
-  BOOST_LIBRARYDIR: C:/Libraries/boost_1_59_0/lib64-msvc-14.0
-  ICU_INCLUDE_DIR: C:/Libraries/icu/include
-  ICU_LIBRARY: C:/Libraries/icu/lib64/icuuc.lib
-  JsonCpp_INCLUDE_DIR: c:/Libraries/jsoncpp_0_10_5/include
-  JsonCpp_LIBRARY: c:/Libraries/jsoncpp_0_10_5/lib/jsoncpp.lib
-  DCMTK_INCLUDE_DIR: C:/Libraries/dcmtk-3.6.1_20150924/include
-  DCMTK_LIBRARY: C:/Libraries/dcmtk-3.6.1_20150924/lib/dcmdata.lib
+      Start-FileDownload http://download.icu-project.org/files/icu4c/58.2/icu4c-58_2-Win64-MSVC2015.zip
+      7z x -bd -oC:\Libraries icu4c-58_2-Win64-MSVC2015.zip
 
-configuration:
-  - Release
+      Start-FileDownload https://github.com/lamyj/appveyor-jsoncpp/releases/download/v1.8.0-2/jsoncpp_1.8.0-2_dynamic_x64.zip jsoncpp.zip
+      7z x -bd -oC:\Libraries jsoncpp.zip
 
-install:
-  # ICU4C
-- ps: Start-FileDownload http://download.icu-project.org/files/icu4c/56.1/icu4c-56_1-Win64-msvc10.zip
-- 7z x -bd -oC:\Libraries icu4c-56_1-Win64-msvc10.zip
-  # JsonCpp
-- ps: Start-FileDownload https://github.com/lamyj/jsoncpp/releases/download/0.10.5/jsoncpp_0_10_5_Win64_msvc14.zip
-- 7z x -bd -oC:\Libraries jsoncpp_0_10_5_Win64_msvc14.zip
-  # DCMTK
-- ps: Start-FileDownload https://github.com/lamyj/dcmtk/releases/download/DCMTK-3.6.1_20150924/dcmtk-3.6.1_20150924_Win64_msvc14_dynamic.zip
-- 7z x -bd -oC:\Libraries dcmtk-3.6.1_20150924_Win64_msvc14_dynamic.zip
+      Start-FileDownload https://github.com/lamyj/appveyor-log4cpp/releases/download/v1.1.1-1/log4cpp_1.1.1-dll_x64.zip log4cpp.zip
+      7z x -bd -oC:\Libraries log4cpp.zip
 
 before_build:
-  - cd c:\projects\odil
-  - md build
-  - cd build
-  - cmake
-    -DBOOST_ROOT="%BOOST_ROOT%" -DBOOST_LIBRARYDIR="%BOOST_LIBRARYDIR%"
-    -DICU_INCLUDE_DIR="%ICU_INCLUDE_DIR%" -DICU_LIBRARY="%ICU_LIBRARY%"
-    -DJsonCpp_INCLUDE_DIR="%JsonCpp_INCLUDE_DIR%" -DJsonCpp_LIBRARY="%JsonCpp_LIBRARY%"
-    -DDCMTK_INCLUDE_DIR="%DCMTK_INCLUDE_DIR%" -DDCMTK_LIBRARY="%DCMTK_LIBRARY%"
-    ..
-
-build:
-  project: C:\projects\odil\build\odil.sln
+  - ps: |
+      ${env:BUILD_DIRECTORY}="${env:APPVEYOR_BUILD_FOLDER}\build"
+      ${env:INSTALL_DIRECTORY}="${env:APPVEYOR_BUILD_FOLDER}\install\${env:APPVEYOR_PROJECT_NAME}"
+
+      New-Item -Path "${env:BUILD_DIRECTORY}" -ItemType "directory"
+      cd "${env:BUILD_DIRECTORY}"
+
+      ${generator} = "Visual Studio 14 2015"
+      if (${env:PLATFORM} -eq "x64") { ${generator} = "${generator} Win64" }
+      cmake `
+        -G "${generator}" -DCMAKE_INSTALL_PREFIX="${env:INSTALL_DIRECTORY}" `
+        -DBUILD_EXAMPLES=ON -DBUILD_WRAPPERS=OFF -DUSE_BUILTIN_DCMTK_GETSCU=OFF `
+        -DBUILD_SHARED_LIBS=ON -DBUILD_TESTING=ON -DWITH_DCMTK=ON `
+        -DBOOST_LIBRARYDIR="${env:BOOST_ROOT}/lib64-msvc-14.0" `
+        -DDCMTK_INCLUDE_DIR="${env:DCMTK_ROOT}/include" -DDCMTK_LIBRARY="${env:DCMTK_ROOT}/lib/dcmdata.lib" `
+        -DDCMDATA_LIBRARY="${env:DCMTK_ROOT}/lib/dcmdata.lib" -DDCMNET_LIBRARY="${env:DCMTK_ROOT}/lib/dcmnet.lib" `
+        -DOFLOG_LIBRARY="${env:DCMTK_ROOT}/lib/oflog.lib" -DOFSTD_LIBRARY="${env:DCMTK_ROOT}/lib/ofstd.lib" `
+        -DICU_INCLUDE_DIR="${env:ICU_ROOT}/include" -DICU_LIBRARY="${env:ICU_ROOT}/lib64/icuuc.lib" `
+        -DJsonCpp_INCLUDE_DIR="${env:JsonCpp_ROOT}/include" -DJsonCpp_LIBRARY="${env:JsonCpp_ROOT}/lib/jsoncpp.lib" `
+        -DLog4Cpp_INCLUDE_DIR="${env:Log4Cpp_ROOT}/include" -DLog4Cpp_LIBRARY="${env:Log4Cpp_ROOT}/lib/log4cpp.lib" `
+        "${env:APPVEYOR_BUILD_FOLDER}"
+
+build_script:
+  - ps: |
+      msbuild "${env:BUILD_DIRECTORY}\${env:APPVEYOR_PROJECT_NAME}.sln" `
+        /m /clp:Verbosity=minimal /p:Configuration=${env:CONFIGURATION}
+      msbuild "${env:BUILD_DIRECTORY}\INSTALL.vcxproj" `
+        /m /clp:Verbosity=minimal /p:Configuration=${env:CONFIGURATION}
+
+before_test:
+  - ps: |
+      & "${env:Python_ROOT}\Scripts\pip.exe" install nose
+
+# nosetests writes on stderr, and powershell treats this as an exception:
+# use cmd instead
+test_script:
+  - cmd: |
+      set PATH=%PATH%;%BOOST_ROOT%\lib64-msvc-14.0;%DCMTK_ROOT%\bin
+      set PATH=%PATH%;%ICU_ROOT%\bin64;%JsonCpp_ROOT%\bin;%Log4Cpp_ROOT%\bin
+      set PATH=%PATH%;%INSTALL_DIRECTORY%\bin
+      set PYTHONPATH=%BUILD_DIRECTORY%\wrappers\%CONFIGURATION%
+      cd %BUILD_DIRECTORY%
+      %Python_ROOT%\python.exe ..\tests\run --no-network -e ".*" --nose=%Python_ROOT%\Scripts\nosetests.exe
+
+after_test:
+  - ps: |
+      Start-FileDownload https://download.microsoft.com/download/f/2/6/f263ac46-1fe9-4ae9-8fd3-21102100ebf5/msxsl.exe
+      .\msxsl.exe `
+        "${env:BUILD_DIRECTORY}\Testing\$(Get-Content Testing\TAG -TotalCount 1)\Test.xml" `
+        "${env:APPVEYOR_BUILD_FOLDER}\ctest-to-junit.xsl" `
+        -o junit.xml
+      $wc = New-Object 'System.Net.WebClient'
+      $wc.UploadFile(
+        "https://ci.appveyor.com/api/testresults/junit/${env:APPVEYOR_JOB_ID}",
+        (Resolve-Path .\junit.xml))
diff --git a/ctest-to-junit.xsl b/ctest-to-junit.xsl
new file mode 100644
index 0000000..910d752
--- /dev/null
+++ b/ctest-to-junit.xsl
@@ -0,0 +1,64 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+The MIT License (MIT)
+
+Copyright (c) 2014, Gregory Boissinot
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+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.
+-->
+<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+    <xsl:output method="xml" indent="yes"/>
+    <xsl:template match="/">
+        <testsuites>
+            <xsl:variable name="buildName" select="//Site/@BuildName"/>
+            <xsl:variable name="numberOfTests" select="count(//Site/Testing/Test)"/>
+            <xsl:variable name="numberOfFailures" select="count(//Site/Testing/Test[@Status!='passed'])"/>
+            <testsuite name="CTest"
+                       tests="{$numberOfTests}" time="0"
+                       failures="{$numberOfFailures}" errors="0"
+                       skipped="0">
+                <xsl:for-each select="//Site/Testing/Test">
+                    <xsl:variable name="testName" select="translate(Name, '-', '_')"/>
+                    <xsl:variable name="duration" select="Results/NamedMeasurement[@name='Execution Time']/Value"/>
+                    <xsl:variable name="status" select="@Status"/>
+                    <xsl:variable name="output" select="Results/Measurement/Value"/>
+                    <xsl:variable name="className" select="translate(Path, '/.', '.')"/>
+                    <testcase classname="projectroot{$className}"
+                              name="{$testName}"
+                              time="{$duration}">
+                        <xsl:choose>
+                            <xsl:when test="@Status='passed'"/>
+                            <xsl:when test="@Status='notrun'">
+                                <skipped/>
+                            </xsl:when>
+                            <xsl:otherwise>
+                                <failure>
+                                    <xsl:value-of select="$output"/>
+                                </failure>
+                            </xsl:otherwise>
+                        </xsl:choose>
+                        <system-out>
+                            <xsl:value-of select="$output"/>
+                        </system-out>
+                    </testcase>
+                </xsl:for-each>
+            </testsuite>
+        </testsuites>
+    </xsl:template>
+</xsl:stylesheet>
\ No newline at end of file
diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt
index 491a8f7..b91c6f2 100644
--- a/examples/CMakeLists.txt
+++ b/examples/CMakeLists.txt
@@ -5,11 +5,15 @@ find_package(JsonCpp REQUIRED)
 include_directories(
     ${CMAKE_SOURCE_DIR}/src ${Boost_INCLUDE_DIRS} ${DCMTK_INCLUDE_DIRS}
     ${JsonCpp_INCLUDE_DIRS})
+
 add_definitions(
     ${DCMTK_DEFINITIONS}
-    -D BOOST_ASIO_DYN_LINK
     -D ODIL_MAJOR_VERSION=${odil_MAJOR_VERSION}
 )
+if(BUILD_SHARED_LIBS)
+    add_definitions(-D BOOST_ALL_DYN_LINK)
+endif()
+
 link_directories(${Boost_LIBRARY_DIRS} ${DCMTK_LIBRARY_DIRS})
 
 file(GLOB_RECURSE examples *.cpp)
diff --git a/examples/find.cpp b/examples/find.cpp
index 7540efc..39458bc 100644
--- a/examples/find.cpp
+++ b/examples/find.cpp
@@ -8,11 +8,11 @@
 
 void print_informations(odil::DataSet const & response)
 {
-    auto const name = response.has("PatientName")?
+    auto const name = (response.has("PatientName") && !response.empty("PatientName"))?
         response.as_string("PatientName", 0):"(no name)";
-    auto const study = response.has("StudyDescription")?
+    auto const study = (response.has("StudyDescription") && !response.empty("StudyDescription"))?
         response.as_string("StudyDescription", 0):"(no description)";
-    auto const date = response.has("StudyDate")?
+    auto const date = (response.has("StudyDate") && !response.empty("StudyDate"))?
         response.as_string("StudyDate", 0):"(no date)";
     std::cout
         << "\"" << name << "\": \"" << study << "\" on " << date << "\n";
@@ -56,6 +56,7 @@ int main()
     std::cout << "Callback\n";
     std::cout << "--------\n\n";
     
+    // We are re-using the query in the next call to "find", so do not move it.
     scu.find(query, print_informations);
 
     std::cout << "\n";
@@ -64,7 +65,8 @@ int main()
     std::cout << "vector\n";
     std::cout << "------\n\n";
     
-    auto const result = scu.find(query);
+    // We are not re-using the query, so move it.
+    auto const result = scu.find(std::move(query));
     for(auto const & dataset: result)
     {
         print_informations(dataset);
diff --git a/examples/genericscp.cpp b/examples/genericscp.cpp
index 3bd0a3c..05ad03d 100644
--- a/examples/genericscp.cpp
+++ b/examples/genericscp.cpp
@@ -5,6 +5,7 @@
 #include "odil/EchoSCP.h"
 #include "odil/FindSCP.h"
 #include "odil/StoreSCP.h"
+#include "odil/NSetSCP.h"
 #include "odil/registry.h"
 #include "odil/SCPDispatcher.h"
 #include "odil/SCP.h"
@@ -13,6 +14,7 @@
 #include "odil/message/CFindRequest.h"
 #include "odil/message/CFindResponse.h"
 #include "odil/message/CStoreRequest.h"
+#include "odil/message/NSetRequest.h"
 
 class FindGenerator: public odil::SCP::DataSetGenerator
 {
@@ -79,15 +81,27 @@ odil::Value::Integer store(odil::message::CStoreRequest const & request)
     return odil::message::Response::Success;
 }
 
+odil::Value::Integer nset(odil::message::NSetRequest const & request)
+{
+    std::cout << "NSetRequest message ID: " << request.get_message_id() <<"\n";
+    std::cout << "NSetRequest requested SOP Class UID: " << request.get_requested_sop_class_uid()<<"\n";
+
+    return odil::message::Response::Success;
+}
+
 int main()
 {
     odil::Association association;
+    while (true)
+   {
+    std::cout << "Waiting for an association on port : 11112..." << std::endl;
+
     association.receive_association(boost::asio::ip::tcp::v4(), 11112);
 
     std::cout
         << "Received association from "
         << association.get_peer_host() << ":" << association.get_peer_port()
-        << "\n";
+        << std::endl ;
 
     auto const & contexts =
         association.get_negotiated_parameters().get_presentation_contexts();
@@ -95,7 +109,7 @@ int main()
     for(auto const & context: contexts)
     {
         std::cout
-            << "    "
+            << "\t"
             << odil::registry::uids_dictionary.at(context.abstract_syntax).name
             << ": "
             << odil::registry::uids_dictionary.at(context.transfer_syntaxes[0]).name
@@ -110,12 +124,15 @@ int main()
     auto find_scp = std::make_shared<odil::FindSCP>(
         association, std::make_shared<FindGenerator>());
     auto store_scp = std::make_shared<odil::StoreSCP>(association, store);
+    auto nset_scp = std::make_shared<odil::NSetSCP>(association, nset);
 
     odil::SCPDispatcher dispatcher(association);
     dispatcher.set_scp(odil::message::Message::Command::C_ECHO_RQ, echo_scp);
     dispatcher.set_scp(odil::message::Message::Command::C_FIND_RQ, find_scp);
     dispatcher.set_scp(
         odil::message::Message::Command::C_STORE_RQ, store_scp);
+    dispatcher.set_scp(
+        odil::message::Message::Command::N_SET_RQ, nset_scp);
 
     bool done = false;
     while(!done)
@@ -139,4 +156,5 @@ int main()
             done = true;
         }
     }
+    }
 }
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index e17e565..0ebd955 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -1,4 +1,4 @@
-find_package(Boost REQUIRED COMPONENTS filesystem system)
+find_package(Boost REQUIRED COMPONENTS date_time filesystem system)
 find_package(ICU REQUIRED)
 find_package(JsonCpp REQUIRED)
 find_package(Log4Cpp REQUIRED)
@@ -9,6 +9,9 @@ endif()
 file(GLOB_RECURSE Header_Files "*.h")
 file(GLOB_RECURSE Source_Files "*.cpp")
 file(GLOB_RECURSE templates "*.txx")
+list(SORT Header_Files)
+list(SORT Source_Files)
+list(SORT templates)
 
 if(NOT WITH_DCMTK)
     set(pattern "${CMAKE_CURRENT_SOURCE_DIR}/odil/dcmtk/[^;]+[;$]")
@@ -24,11 +27,16 @@ GroupFiles(Source_Files)
 include_directories(
     ${CMAKE_CURRENT_SOURCE_DIR} ${Boost_INCLUDE_DIRS} ${DCMTK_INCLUDE_DIRS}
 ${ICU_INCLUDE_DIRS} ${Log4Cpp_INCLUDE_DIRS} ${JsonCpp_INCLUDE_DIRS})
+
 add_definitions(
     ${DCMTK_DEFINITIONS}
     -D BOOST_ASIO_SEPARATE_COMPILATION
     -D ODIL_MAJOR_VERSION=${odil_MAJOR_VERSION}
 )
+if(BUILD_SHARED_LIBS)
+    add_definitions(-D BOOST_ALL_DYN_LINK)
+endif()
+
 link_directories(${Boost_LIBRARY_DIRS} ${DCMTK_LIBRARY_DIRS})
 
 add_library(libodil ${Source_Files} ${Header_Files} ${templates})
diff --git a/src/odil/Association.cpp b/src/odil/Association.cpp
index 001ab87..b4dd53f 100644
--- a/src/odil/Association.cpp
+++ b/src/odil/Association.cpp
@@ -195,7 +195,7 @@ bool
 Association
 ::is_associated() const
 {
-    return this->_state_machine.get_transport().is_open();
+    return this->_state_machine.get_transport().is_open() && this->_state_machine.get_state() == odil::dul::StateMachine::State::Sta6 ;
 }
 
 void
@@ -442,13 +442,13 @@ Association
         }
 
         Reader reader(data_stream, transfer_syntax_it->second);
-        auto const data_set = reader.read_data_set();
+        auto data_set = reader.read_data_set();
 
-        return message::Message(command_set, data_set);
+        return message::Message(std::move(command_set), std::move(data_set));
     }
     else
     {
-        return message::Message(command_set);
+        return message::Message(std::move(command_set));
     }
 }
 
diff --git a/src/odil/Association.h b/src/odil/Association.h
index b49ca25..81ce872 100644
--- a/src/odil/Association.h
+++ b/src/odil/Association.h
@@ -19,6 +19,7 @@
 #include "odil/AssociationParameters.h"
 #include "odil/dul/StateMachine.h"
 #include "odil/message/Message.h"
+#include "odil/odil.h"
 
 namespace odil
 {
@@ -26,7 +27,7 @@ namespace odil
 /**
  * @brief Association.
  */
-class Association
+class ODIL_API Association
 {
 public:
     /// @brief Association result (ITU-T X.227, PS 3.8, 7.1.1.7 and PS 3.8, 9.3.4).
@@ -142,7 +143,7 @@ public:
     /// @brief Test whether the object is currently associated to its peer.
     bool is_associated() const;
 
-    /// @brief Request an association with the peer.
+    /// @brief Request an association with the peer. Throws an exception if the endpoint can not be reached.
     void associate();
 
     /// @brief Receive an association from a peer.
@@ -200,7 +201,7 @@ private:
  * @brief Exception reported when receiving a message after the association was
  * released.
  */
-class AssociationReleased: public Exception
+class ODIL_API AssociationReleased: public Exception
 {
 public:
     AssociationReleased()
@@ -214,7 +215,7 @@ public:
  * @brief Exception reported when receiving a message after the association was
  * aborted.
  */
-class AssociationAborted: public Exception
+class ODIL_API AssociationAborted: public Exception
 {
 public:
     /// @brief Source of the error.
diff --git a/src/odil/AssociationAcceptor.h b/src/odil/AssociationAcceptor.h
index 815136c..c032431 100644
--- a/src/odil/AssociationAcceptor.h
+++ b/src/odil/AssociationAcceptor.h
@@ -14,6 +14,7 @@
 
 #include "odil/AssociationParameters.h"
 #include "odil/Exception.h"
+#include "odil/odil.h"
 
 namespace odil
 {
@@ -35,11 +36,12 @@ typedef
  * and the roles of each presentation context, do not check identity,
  * keep maximum length.
  */
+ODIL_API
 AssociationParameters
 default_association_acceptor(AssociationParameters const & input);
 
 /// @brief Exception reported when an incoming association is rejected.
-struct AssociationRejected: public Exception
+struct ODIL_API AssociationRejected: public Exception
 {
 public:
     /// @brief Constructor.
diff --git a/src/odil/AssociationParameters.cpp b/src/odil/AssociationParameters.cpp
index 6d9d605..aabb590 100644
--- a/src/odil/AssociationParameters.cpp
+++ b/src/odil/AssociationParameters.cpp
@@ -115,7 +115,9 @@ AssociationParameters
 AssociationParameters
 ::AssociationParameters(pdu::AAssociateRQ const & pdu)
 : _called_ae_title(""), _calling_ae_title(""), _presentation_contexts(),
-  _user_identity(), _maximum_length(16384)
+  _user_identity({UserIdentity::Type::None, "", ""}), _maximum_length(16384),
+  _maximum_number_operations_invoked(1), _maximum_number_operations_performed(1),
+  _sop_class_extended_negotiation(), _sop_class_common_extended_negotiation()
 {
     this->set_called_ae_title(pdu.get_called_ae_title());
     this->set_calling_ae_title(pdu.get_calling_ae_title());
@@ -202,6 +204,10 @@ AssociationParameters
 AssociationParameters
 ::AssociationParameters(
     pdu::AAssociateAC const & pdu, AssociationParameters const & request)
+: _called_ae_title(""), _calling_ae_title(""), _presentation_contexts(),
+  _user_identity({UserIdentity::Type::None, "", ""}), _maximum_length(16384),
+  _maximum_number_operations_invoked(1), _maximum_number_operations_performed(1),
+  _sop_class_extended_negotiation(), _sop_class_common_extended_negotiation()
 {
     // Calling and Called AE titles are not meaningful in A-ASSOCIATE-AC
     this->set_called_ae_title(request.get_called_ae_title());
diff --git a/src/odil/AssociationParameters.h b/src/odil/AssociationParameters.h
index d11d582..bb53944 100644
--- a/src/odil/AssociationParameters.h
+++ b/src/odil/AssociationParameters.h
@@ -13,6 +13,7 @@
 #include <string>
 #include <vector>
 
+#include "odil/odil.h"
 #include "odil/pdu/AAssociateAC.h"
 #include "odil/pdu/AAssociateRQ.h"
 #include "odil/pdu/SOPClassCommonExtendedNegotiation.h"
@@ -22,14 +23,14 @@ namespace odil
 {
 
 /// @brief Encapsulate association parameters
-class AssociationParameters
+class ODIL_API AssociationParameters
 {
 public:
     /**
      * @brief Presentation Context, cf. PS 3.8, 9.3.2.2, PS 3.8, 9.3.3.2,
      * PS 3.7, D.3.3.4.1 and PS 3.7 D.3.3.4.2.
      */
-    struct PresentationContext
+    struct ODIL_API PresentationContext
     {
         /// @brief Result of the presentation context negotiation.
         enum class Result
@@ -71,7 +72,7 @@ public:
     };
 
     /// @brief User Identity, cf. PS3.8 D.3.3.7
-    struct UserIdentity
+    struct ODIL_API UserIdentity
     {
         /// @brief User identity type.
         enum class Type
diff --git a/src/odil/BasicDirectoryCreator.h b/src/odil/BasicDirectoryCreator.h
index e84354f..26eab26 100644
--- a/src/odil/BasicDirectoryCreator.h
+++ b/src/odil/BasicDirectoryCreator.h
@@ -16,6 +16,7 @@
 #include <vector>
 
 #include "odil/DataSet.h"
+#include "odil/odil.h"
 #include "odil/Tag.h"
 #include "odil/Writer.h"
 
@@ -23,7 +24,7 @@ namespace odil
 {
 
 /// @brief Write a Basic Directory (i.e. DICOMDIR) object to the disk.
-class BasicDirectoryCreator
+class ODIL_API BasicDirectoryCreator
 {
 public:
     /// @brief The tag and its associated type in the record.
diff --git a/src/odil/DataSet.cpp b/src/odil/DataSet.cpp
index 493c788..91d2650 100644
--- a/src/odil/DataSet.cpp
+++ b/src/odil/DataSet.cpp
@@ -32,103 +32,96 @@ void
 DataSet
 ::add(Tag const & tag, Element const & element)
 {
-    this->_elements[tag] = element;
-}
-
-
-void
-DataSet
-::add(Tag const & tag, VR vr)
-{
-    Value value;
-
-    if(vr == VR::UNKNOWN)
-    {
-        vr = as_vr(tag);
-    }
-
-    if(::odil::is_int(vr))
-    {
-        value = Value::Integers();
-    }
-    else if(::odil::is_real(vr))
-    {
-        value = Value::Reals();
-    }
-    else if(::odil::is_string(vr))
-    {
-        value = Value::Strings();
-    }
-    else if(::odil::is_binary(vr))
-    {
-        value = Value::Binary();
-    }
-    else if(vr == VR::SQ)
+    auto const iterator = this->_elements.find(tag);
+    if(iterator == this->_elements.end())
     {
-        value = Value::DataSets();
+        // WARNING: std::map<K,V>::emplace does not exist on old compilers.
+        // This is however a case of non-mandatory copy elision, so the copy
+        // constructor of element should only be called once.
+        this->_elements.insert(std::make_pair(tag, element));
+        //this->_elements.emplace(tag, element);
     }
     else
     {
-        throw Exception("Unknown VR: "+::odil::as_string(vr));
-    }
-
-    this->add(tag, Element(value, vr));
-}
-
-void
-DataSet
-::add(Tag const & tag, Value::Integers const & value, VR vr)
-{
-    if(vr == VR::UNKNOWN)
-    {
-        vr = as_vr(tag);
+        iterator->second = element;
     }
-    this->add(tag, Element(value, vr));
 }
 
 void
 DataSet
-::add(Tag const & tag, Value::Reals const & value, VR vr)
+::add(Tag const & tag, Element && element)
 {
-    if(vr == VR::UNKNOWN)
+    auto const iterator = this->_elements.find(tag);
+    if(iterator == this->_elements.end())
     {
-        vr = as_vr(tag);
+        // WARNING: std::map<K,V>::emplace does not exist on old compilers.
+        this->_elements.insert(std::make_pair(tag, std::move(element)));
+        //this->_elements.emplace(tag, std::move(element));
     }
-    this->add(tag, Element(value, vr));
-}
-
-void
-DataSet
-::add(Tag const & tag, Value::Strings const & value, VR vr)
-{
-    if(vr == VR::UNKNOWN)
+    else
     {
-        vr = as_vr(tag);
+        iterator->second = element;
     }
-    this->add(tag, Element(value, vr));
 }
 
 void
 DataSet
-::add(Tag const & tag, Value::DataSets const & value, VR vr)
+::add(Tag const & tag, VR vr)
 {
     if(vr == VR::UNKNOWN)
     {
         vr = as_vr(tag);
     }
-    this->add(tag, Element(value, vr));
-}
 
-void
-DataSet
-::add(Tag const & tag, Value::Binary const & value, VR vr)
-{
-    if(vr == VR::UNKNOWN)
-    {
-        vr = as_vr(tag);
+    this->add(tag, Element(vr));
+}
+
+#define ODIL_DATASET_ADD(type) \
+    void\
+    DataSet\
+    ::add(\
+        Tag const & tag, Value::type const & value, VR vr)\
+        { \
+            if(vr == VR::UNKNOWN)\
+            {\
+                vr = as_vr(tag);\
+            }\
+            this->add(tag, Element(value, vr));\
+        }\
+    void\
+    DataSet\
+    ::add(\
+        Tag const & tag, Value::type && value, VR vr)\
+    { \
+        if(vr == VR::UNKNOWN)\
+        {\
+            vr = as_vr(tag);\
+        }\
+        this->add(tag, Element(std::move(value), vr));\
+    }\
+    void\
+    DataSet\
+    ::add(\
+        Tag const & tag, \
+        std::initializer_list<Value::type::value_type> const & value, VR vr)\
+    { \
+        if(vr == VR::UNKNOWN)\
+        {\
+            vr = as_vr(tag);\
+        }\
+        this->add(tag, Element(value, vr));\
     }
-    this->add(tag, Element(value, vr));
-}
+    /*
+     * No need for for a rvalue reference version of std::initializer_list:
+     * copying a std::initializer_list does not copy the underlying objects.
+     */
+
+    ODIL_DATASET_ADD(Integers);
+    ODIL_DATASET_ADD(Reals);
+    ODIL_DATASET_ADD(Strings);
+    ODIL_DATASET_ADD(DataSets);
+    ODIL_DATASET_ADD(Binary);
+#undef ODIL_DATASET_ADD
 
 void
 DataSet
@@ -144,43 +137,8 @@ DataSet
 void
 DataSet
 ::add(
-    Tag const & tag, std::initializer_list<Value::Integer> const & value, VR vr)
-{
-    if(vr == VR::UNKNOWN)
-    {
-        vr = as_vr(tag);
-    }
-    this->add(tag, Element(value, vr));
-}
-
-void
-DataSet
-::add(
-    Tag const & tag, std::initializer_list<Value::Real> const & value, VR vr)
-{
-    if(vr == VR::UNKNOWN)
-    {
-        vr = as_vr(tag);
-    }
-    this->add(tag, Element(value, vr));
-}
-
-void
-DataSet
-::add(
-    Tag const & tag, std::initializer_list<Value::String> const & value, VR vr)
-{
-    if(vr == VR::UNKNOWN)
-    {
-        vr = as_vr(tag);
-    }
-    this->add(tag, Element(value, vr));
-}
-
-void
-DataSet
-::add(
-    Tag const & tag, std::initializer_list<DataSet> const & value, VR vr)
+    Tag const & tag,
+    std::initializer_list<std::initializer_list<uint8_t>> const & value, VR vr)
 {
     if(vr == VR::UNKNOWN)
     {
@@ -195,7 +153,7 @@ DataSet
 {
     if(!this->has(tag))
     {
-        throw Exception("No such element");
+        throw Exception("No such element " + std::string( tag ));
     }
 
     this->_elements.erase(tag);
@@ -222,7 +180,7 @@ DataSet
     ElementMap::const_iterator const it = this->_elements.find(tag);
     if(it == this->_elements.end())
     {
-        throw Exception("No such element");
+        throw Exception("No such element " + std::string( tag ));
     }
 
     return it->second;
@@ -235,7 +193,7 @@ DataSet
     ElementMap::iterator it = this->_elements.find(tag);
     if(it == this->_elements.end())
     {
-        throw Exception("No such element");
+        throw Exception("No such element " + std::string( tag ));
     }
 
     return it->second;
@@ -406,7 +364,7 @@ DataSet
     ElementMap::const_iterator const it = this->_elements.find(tag);
     if(it == this->_elements.end())
     {
-        throw Exception("No such element");
+        throw Exception("No such element "+  std::string(tag) );
     }
 
     return it->second.vr;
@@ -419,7 +377,7 @@ DataSet
     ElementMap::const_iterator const it = this->_elements.find(tag);
     if(it == this->_elements.end())
     {
-        throw Exception("No such element");
+        throw Exception("No such element " + std::string( tag ) );
     }
 
     return it->second.empty();
@@ -432,12 +390,26 @@ DataSet
     ElementMap::const_iterator const it = this->_elements.find(tag);
     if(it == this->_elements.end())
     {
-        throw Exception("No such element");
+        throw Exception("No such element " + std::string( tag ));
     }
 
     return it->second.size();
 }
 
+DataSet::const_iterator
+DataSet
+::begin() const
+{
+    return this->_elements.begin();
+}
+
+DataSet::const_iterator
+DataSet
+::end() const
+{
+    return this->_elements.end();
+}
+
 bool
 DataSet
 ::operator==(DataSet const & other) const
@@ -452,6 +424,19 @@ DataSet
     return !(*this == other);
 }
 
+void
+DataSet
+::clear(Tag const & tag)
+{
+    ElementMap::iterator const it = this->_elements.find(tag);
+    if(it == this->_elements.end())
+    {
+        throw Exception("No such element: "+std::string(tag));
+    }
+
+    it->second.clear();
+}
+
 std::string const &
 DataSet
 ::get_transfer_syntax() const
diff --git a/src/odil/DataSet.h b/src/odil/DataSet.h
index ff59454..c49c1c7 100644
--- a/src/odil/DataSet.h
+++ b/src/odil/DataSet.h
@@ -17,6 +17,7 @@
 #include <vector>
 
 #include "odil/Element.h"
+#include "odil/odil.h"
 #include "odil/Value.h"
 
 namespace odil
@@ -25,61 +26,61 @@ namespace odil
 /**
  * @brief DICOM Data set.
  */
-class DataSet
+class ODIL_API DataSet
 {
 public:
     /// @brief Create an empty data set.
     explicit DataSet(std::string const & transfer_syntax="");
 
-    /// @brief Add an element to the dataset.
-    void add(Tag const & tag, Element const & element);
-
-    /// @brief Add an empty element to the dataset.
-    void add(Tag const & tag, VR vr=VR::UNKNOWN);
-
-    /// @brief Add an element to the dataset.
-    void add(
-        Tag const & tag, Value::Integers const & value, VR vr=VR::UNKNOWN);
-
-    /// @brief Add an element to the dataset.
-    void add(
-        Tag const & tag, Value::Reals const & value, VR vr=VR::UNKNOWN);
-
-    /// @brief Add an element to the dataset.
-    void add(
-        Tag const & tag, Value::Strings const & value, VR vr=VR::UNKNOWN);
+    /** @addtogroup default_operations Default class operations
+     * @{
+     */
+    ~DataSet() =default;
+    DataSet(DataSet const &) =default;
+    DataSet(DataSet &&) =default;
+    DataSet & operator=(DataSet const &) =default;
+    DataSet & operator=(DataSet &&) =default;
+    /// @}
 
     /// @brief Add an element to the dataset.
-    void add(
-        Tag const & tag, Value::DataSets const & value, VR vr=VR::UNKNOWN);
+    void add(Tag const & tag, Element const & element);
 
     /// @brief Add an element to the dataset.
-    void add(
-        Tag const & tag, Value::Binary const & value, VR vr=VR::UNKNOWN);
+    void add(Tag const & tag, Element && element);
 
-    /// @brief Add an element to the dataset.
-    void add(
-        Tag const & tag, std::initializer_list<int> const & value,
-        VR vr=VR::UNKNOWN);
+    /// @brief Add an empty element to the dataset.
+    void add(Tag const & tag, VR vr=VR::UNKNOWN);
 
-    /// @brief Add an element to the dataset.
-    void add(
-        Tag const & tag, std::initializer_list<Value::Integer> const & value,
+#define ODIL_DATASET_ADD(type) \
+    void add(\
+        Tag const & tag, Value::type const & value, VR vr=VR::UNKNOWN);\
+    void add(\
+        Tag const & tag, Value::type && value, VR vr=VR::UNKNOWN); \
+    void add(\
+        Tag const & tag, \
+        std::initializer_list<Value::type::value_type> const & value, \
         VR vr=VR::UNKNOWN);
+    /*
+     * No need for for a rvalue reference version of std::initializer_list:
+     * copying a std::initializer_list does not copy the underlying objects.
+     */
 
-    /// @brief Add an element to the dataset.
-    void add(
-        Tag const & tag, std::initializer_list<Value::Real> const & value,
-        VR vr=VR::UNKNOWN);
+    ODIL_DATASET_ADD(Integers);
+    ODIL_DATASET_ADD(Reals);
+    ODIL_DATASET_ADD(Strings);
+    ODIL_DATASET_ADD(DataSets);
+    ODIL_DATASET_ADD(Binary);
+#undef ODIL_DATASET_ADD
 
     /// @brief Add an element to the dataset.
     void add(
-        Tag const & tag, std::initializer_list<Value::String> const & value,
+        Tag const & tag, std::initializer_list<int> const & value,
         VR vr=VR::UNKNOWN);
 
     /// @brief Add an element to the dataset.
     void add(
-        Tag const & tag, std::initializer_list<DataSet> const & value,
+        Tag const & tag,
+        std::initializer_list<std::initializer_list<uint8_t>> const & value,
         VR vr=VR::UNKNOWN);
 
     /**
@@ -198,10 +199,10 @@ public:
     typedef std::map<Tag, Element>::const_iterator const_iterator;
 
     /// @brief Return an iterator to the start of the elements.
-    const_iterator begin() const { return this->_elements.begin(); }
+    const_iterator begin() const;
 
     /// @brief Return an iterator to the end of the elements.
-    const_iterator end() const { return this->_elements.end(); }
+    const_iterator end() const;
 
     /// @brief Equality test.
     bool operator==(DataSet const & other) const;
@@ -209,6 +210,9 @@ public:
     /// @brief Difference test.
     bool operator!=(DataSet const & other) const;
 
+    /// @brief Clear the element (data_set.empty(tag) will be true).
+    void clear(Tag const & tag);
+
     /// @brief Return the current transfer syntax.
     std::string const & get_transfer_syntax() const;
 
diff --git a/src/odil/EchoSCP.cpp b/src/odil/EchoSCP.cpp
index e80e24b..e9872fc 100644
--- a/src/odil/EchoSCP.cpp
+++ b/src/odil/EchoSCP.cpp
@@ -58,7 +58,21 @@ EchoSCP
 ::operator()(message::Message const & message)
 {
     message::CEchoRequest const request(message);
+    this->operator()(request);
+}
 
+void
+EchoSCP
+::operator()(message::Message && message)
+{
+    message::CEchoRequest const request(std::move(message));
+    this->operator()(request);
+}
+
+void
+EchoSCP
+::operator()(message::CEchoRequest const & request)
+{
     Value::Integer status=message::CEchoResponse::Success;
     DataSet status_fields;
 
diff --git a/src/odil/EchoSCP.h b/src/odil/EchoSCP.h
index 5fa0433..b5e30aa 100644
--- a/src/odil/EchoSCP.h
+++ b/src/odil/EchoSCP.h
@@ -12,6 +12,7 @@
 #include <functional>
 
 #include "odil/Association.h"
+#include "odil/odil.h"
 #include "odil/SCP.h"
 #include "odil/Value.h"
 #include "odil/message/CEchoRequest.h"
@@ -21,7 +22,7 @@ namespace odil
 {
 
 /// @brief SCP for C-Echo services.
-class EchoSCP: public SCP
+class ODIL_API EchoSCP: public SCP
 {
 public:
     /**
@@ -48,8 +49,12 @@ public:
     /// @brief Process a C-Echo request.
     virtual void operator()(message::Message const & message);
 
+    /// @brief Process a C-Echo request.
+    virtual void operator()(message::Message && message);
+
 private:
     Callback _callback;
+    void operator()(message::CEchoRequest const & request);
 };
 
 }
diff --git a/src/odil/EchoSCU.h b/src/odil/EchoSCU.h
index 5ad96d8..a912924 100644
--- a/src/odil/EchoSCU.h
+++ b/src/odil/EchoSCU.h
@@ -10,13 +10,14 @@
 #define _94f3b347_1f95_49ab_83f6_8710f5a3ad67
 
 #include "odil/Association.h"
+#include "odil/odil.h"
 #include "odil/SCU.h"
 
 namespace odil
 {
 
 /// @brief SCU for the C-ECHO services.
-class EchoSCU: public SCU
+class ODIL_API EchoSCU: public SCU
 {
 public:
     /// @brief Constructor
diff --git a/src/odil/Element.cpp b/src/odil/Element.cpp
index 4ac0d7d..eb749bc 100644
--- a/src/odil/Element.cpp
+++ b/src/odil/Element.cpp
@@ -8,6 +8,10 @@
 
 #include "odil/Element.h"
 
+#include <initializer_list>
+#include <utility>
+
+#include "odil/Exception.h"
 #include "odil/Value.h"
 #include "odil/DataSet.h"
 
@@ -15,78 +19,93 @@ namespace odil
 {
 
 Element
-::Element(Value const & value, VR const & vr)
-: _value(value), vr(vr)
-{
-    // Nothing else
+::Element(VR const & vr)
+: _value(Value::Integers()), vr(vr)
+{
+    if(odil::is_int(vr))
+    {
+        this->_value = Value::Integers();
+    }
+    else if(odil::is_real(vr))
+    {
+        this->_value = Value::Reals();
+    }
+    else if(odil::is_string(vr))
+    {
+        this->_value = Value::Strings();
+    }
+    else if(vr == VR::SQ)
+    {
+        this->_value = Value::DataSets();
+    }
+    else if(odil::is_binary(vr))
+    {
+        this->_value = Value::Binary();
+    }
+    else
+    {
+        throw Exception("Unknown VR type");
+    }
 }
 
 Element
-::Element(Value::Integers const & value, VR const & vr)
-: _value(value), vr(vr)
-{
-    // Nothing else
-}
-
-Element
-::Element(Value::Reals const & value, VR const & vr)
-: _value(value), vr(vr)
-{
-    // Nothing else
-}
-
-Element
-::Element(Value::Strings const & value, VR const & vr)
+::Element(Value const & value, VR const & vr)
 : _value(value), vr(vr)
 {
     // Nothing else
 }
 
 Element
-::Element(Value::DataSets const & value, VR const & vr)
-: _value(value), vr(vr)
+::Element(Value && value, VR const & vr)
+: _value(std::move(value)), vr(vr)
 {
     // Nothing else
 }
 
-Element
-::Element(Value::Binary const & value, VR const & vr)
-: _value(value), vr(vr)
-{
-    // Nothing else
-}
+#define ODIL_ELEMENT_CONSTRUCTORS(type) \
+    Element\
+    ::Element(Value::type const & value, VR const & vr)\
+    : vr(vr), _value(value)\
+    {\
+    }\
+    \
+    Element\
+    ::Element(Value::type && value, VR const & vr)\
+    : vr(vr), _value(std::move(value))\
+    {\
+    }\
+    \
+    Element\
+    ::Element(\
+        std::initializer_list<Value::type::value_type> const & value, \
+        VR const & vr)\
+    : vr(vr), _value(value)\
+    {\
+    }
+    /*
+     * No need for for a rvalue reference version of std::initializer_list:
+     * copying a std::initializer_list does not copy the underlying objects.
+     */
+
+    ODIL_ELEMENT_CONSTRUCTORS(Integers);
+    ODIL_ELEMENT_CONSTRUCTORS(Reals);
+    ODIL_ELEMENT_CONSTRUCTORS(Strings);
+    ODIL_ELEMENT_CONSTRUCTORS(DataSets);
+    ODIL_ELEMENT_CONSTRUCTORS(Binary);
+#undef ODIL_ELEMENT_CONSTRUCTORS
 
 Element
 ::Element(std::initializer_list<int> const & value, VR const & vr)
-: _value(value), vr(vr)
+: vr(vr), _value(value)
 {
     // Nothing else
 }
 
 Element
-::Element(std::initializer_list<Value::Integer> const & value, VR const & vr)
-: _value(value), vr(vr)
-{
-    // Nothing else
-}
-
-Element
-::Element(std::initializer_list<Value::Real> const & value, VR const & vr)
-: _value(value), vr(vr)
-{
-    // Nothing else
-}
-
-Element
-::Element(std::initializer_list<Value::String> const & value, VR const & vr)
-: _value(value), vr(vr)
-{
-    // Nothing else
-}
-
-Element
-::Element(std::initializer_list<DataSet> const & value, VR const & vr)
-: _value(value), vr(vr)
+::Element(
+    std::initializer_list<std::initializer_list<uint8_t>> const & value,
+    VR const & vr)
+: vr(vr), _value(value)
 {
     // Nothing else
 }
@@ -95,18 +114,14 @@ bool
 Element
 ::empty() const
 {
-    return (
-        (this->_value.get_type() == Value::Type::Empty) ||
-        apply_visitor(Empty(), this->_value));
+    return this->_value.empty();
 }
 
 std::size_t
 Element
 ::size() const
 {
-    return (
-        (this->_value.get_type() == Value::Type::Empty)?0:
-        apply_visitor(Size(), this->_value));
+    return this->_value.size();
 }
 
 Value const &
@@ -233,4 +248,12 @@ Element
     return !(*this == other);
 }
 
+void
+Element
+::clear()
+{
+    this->_value.clear();
+}
+
 }
+
diff --git a/src/odil/Element.h b/src/odil/Element.h
index 917cab2..3b0ccfb 100644
--- a/src/odil/Element.h
+++ b/src/odil/Element.h
@@ -12,6 +12,7 @@
 #include <cstddef>
 #include <initializer_list>
 
+#include "odil/odil.h"
 #include "odil/Tag.h"
 #include "odil/Value.h"
 #include "odil/VR.h"
@@ -22,54 +23,56 @@ namespace odil
 /**
  * @brief Element of a DICOM data set.
  */
-class Element
+class ODIL_API Element
 {
 public:
 
     /// @brief VR of the element.
     VR vr;
 
-    /// @brief Constructor.
-    Element(Value const & value=Value(), VR const & vr=VR::INVALID);
-
-    /// @brief Constructor.
-    Element(Value::Integers const & value, VR const & vr=VR::INVALID);
+    /// @brief Constructor using the VR to create an according empty container.
+    Element(VR const & vr);
 
     /// @brief Constructor.
-    Element(Value::Reals const & value, VR const & vr=VR::INVALID);
+    Element(Value const & value, VR const & vr);
 
     /// @brief Constructor.
-    Element(Value::Strings const & value, VR const & vr=VR::INVALID);
+    Element(Value && value, VR const & vr);
 
-    /// @brief Constructor.
-    Element(Value::DataSets const & value, VR const & vr=VR::INVALID);
+#define ODIL_ELEMENT_CONSTRUCTORS(type) \
+    Element(Value::type const & value, VR const & vr=VR::INVALID); \
+    Element(Value::type && value, VR const & vr=VR::INVALID); \
+    Element(\
+        std::initializer_list<Value::type::value_type> const & value, \
+        VR const & vr=VR::INVALID);
+    /*
+     * No need for for a rvalue reference version of std::initializer_list:
+     * copying a std::initializer_list does not copy the underlying objects.
+     */
 
-    /// @brief Constructor.
-    Element(Value::Binary const & value, VR const & vr=VR::INVALID);
+    ODIL_ELEMENT_CONSTRUCTORS(Integers);
+    ODIL_ELEMENT_CONSTRUCTORS(Reals);
+    ODIL_ELEMENT_CONSTRUCTORS(Strings);
+    ODIL_ELEMENT_CONSTRUCTORS(DataSets);
+    ODIL_ELEMENT_CONSTRUCTORS(Binary);
+#undef ODIL_ELEMENT_CONSTRUCTORS
 
-    /// @brief Constructor.
     Element(
         std::initializer_list<int> const & value, VR const & vr=VR::INVALID);
 
-    /// @brief Constructor.
     Element(
-        std::initializer_list<Value::Integer> const & value,
+        std::initializer_list<std::initializer_list<uint8_t>> const & value,
         VR const & vr=VR::INVALID);
 
-    /// @brief Constructor.
-    Element(
-        std::initializer_list<Value::Real> const & value,
-        VR const & vr=VR::INVALID);
-
-    /// @brief Constructor.
-    Element(
-        std::initializer_list<Value::String> const & value,
-        VR const & vr=VR::INVALID);
-
-    /// @brief Constructor.
-    Element(
-        std::initializer_list<DataSet> const & value,
-        VR const & vr=VR::INVALID);
+    /** @addtogroup default_operations Default class operations
+     * @{
+     */
+    ~Element() =default;
+    Element(Element const &) =default;
+    Element(Element &&) =default;
+    Element & operator=(Element const &) =default;
+    Element & operator=(Element &&) =default;
+    /// @}
 
     /// @brief Test whether the element is empty.
     bool empty() const;
@@ -170,31 +173,11 @@ public:
 
     /// @brief Difference test
     bool operator!=(Element const & other) const;
+    
+    /// @brief Clear the element (element.empty() will be true).
+    void clear();
 
 private:
-    struct Empty
-    {
-        typedef bool result_type;
-
-        template<typename T>
-        bool operator()(T const & container) const
-        {
-            return container.empty();
-        }
-    };
-
-    struct Size
-    {
-        typedef std::size_t result_type;
-
-        template<typename T>
-        std::size_t operator()(T const & container) const
-        {
-            return container.size();
-        }
-    };
-
-
     Value _value;
 };
 
@@ -211,3 +194,4 @@ apply_visitor(TVisitor const & visitor, Element const & element);
 #include "odil/Element.txx"
 
 #endif // _9c3d8f32_0310_4e3a_b5d2_6d69f229a2cf
+
diff --git a/src/odil/ElementsDictionary.h b/src/odil/ElementsDictionary.h
index 0179767..929b622 100644
--- a/src/odil/ElementsDictionary.h
+++ b/src/odil/ElementsDictionary.h
@@ -12,63 +12,64 @@
 #include <map>
 #include <string>
 
-#include <odil/Tag.h>
+#include "odil/odil.h"
+#include "odil/Tag.h"
 
 namespace odil
 {
 
-    /// @brief Key of a dictionary of DICOM elements.
-    class ElementsDictionaryKey
+/// @brief Key of a dictionary of DICOM elements.
+class ODIL_API ElementsDictionaryKey
+{
+public:
+    /// @brief Type of the key.
+    enum class Type
     {
-    public:
-        /// @brief Type of the key.
-        enum class Type
-        {
-            Tag,
-            String,
-            None
-        };
+        Tag,
+        String,
+        None
+    };
 
-        /// @brief Create a key with type equal to None.
-        ElementsDictionaryKey();
+    /// @brief Create a key with type equal to None.
+    ElementsDictionaryKey();
 
-        /// @brief Create a key with type equal to Tag.
-        ElementsDictionaryKey(Tag const & value);
+    /// @brief Create a key with type equal to Tag.
+    ElementsDictionaryKey(Tag const & value);
 
-        /// @brief Create a key with type equal to String.
-        ElementsDictionaryKey(std::string const & value);
+    /// @brief Create a key with type equal to String.
+    ElementsDictionaryKey(std::string const & value);
 
-        /// @brief Return the type.
-        Type const & get_type() const;
+    /// @brief Return the type.
+    Type const & get_type() const;
 
-        /// @brief Return the tag value or raise an exception if type is not Tag.
-        Tag const & get_tag() const;
+    /// @brief Return the tag value or raise an exception if type is not Tag.
+    Tag const & get_tag() const;
 
-        /// @brief Return the string value or raise an exception if type is not String.
-        std::string const & get_string() const;
+    /// @brief Return the string value or raise an exception if type is not String.
+    std::string const & get_string() const;
 
-        /// @brief Set the type to Tag.
-        void set(Tag const value);
+    /// @brief Set the type to Tag.
+    void set(Tag const value);
 
-        /// @brief Set the type to String.
-        void set(std::string const & value);
+    /// @brief Set the type to String.
+    void set(std::string const & value);
 
-        /// @brief Comparator.
-        bool operator<(ElementsDictionaryKey const & other) const;
+    /// @brief Comparator.
+    bool operator<(ElementsDictionaryKey const & other) const;
 
-        /// @brief Comparator.
-        bool operator==(ElementsDictionaryKey const & other) const;
+    /// @brief Comparator.
+    bool operator==(ElementsDictionaryKey const & other) const;
 
-    private:
-        Type _type;
-        Tag _tag;
-        std::string _string;
-    };
+private:
+    Type _type;
+    Tag _tag;
+    std::string _string;
+};
 
 /**
  * @brief Entry in a dictionary of DICOM elements.
  */
-struct ElementsDictionaryEntry
+struct ODIL_API ElementsDictionaryEntry
 {
     /// @brief Full name.
     std::string name;
@@ -91,6 +92,7 @@ struct ElementsDictionaryEntry
 typedef
     std::map<ElementsDictionaryKey, ElementsDictionaryEntry> ElementsDictionary;
 
+ODIL_API
 ElementsDictionary::const_iterator
 find(ElementsDictionary const & dictionary, Tag const & tag);
 
diff --git a/src/odil/Exception.h b/src/odil/Exception.h
index 89583d6..12e347d 100644
--- a/src/odil/Exception.h
+++ b/src/odil/Exception.h
@@ -12,11 +12,13 @@
 #include <exception>
 #include <string>
 
+#include "odil/odil.h"
+
 namespace odil
 {
 
 /// @brief Base class for odil exceptions.
-class Exception: public std::exception
+class ODIL_API Exception: public std::exception
 {
 public: 
     /// @brief Message string constructor.
diff --git a/src/odil/FindSCP.cpp b/src/odil/FindSCP.cpp
index b45e2b8..b7d9696 100644
--- a/src/odil/FindSCP.cpp
+++ b/src/odil/FindSCP.cpp
@@ -61,7 +61,21 @@ FindSCP
 ::operator()(message::Message const & message)
 {
     message::CFindRequest const request(message);
+    this->operator()(request);
+}
 
+void
+FindSCP
+::operator()(message::Message && message)
+{
+    message::CFindRequest request(std::move(message));
+    this->operator()(request);
+}
+
+void
+FindSCP
+::operator()(message::CFindRequest const & request)
+{
     Value::Integer final_status = message::CFindResponse::Success;
     DataSet status_fields;
 
@@ -70,10 +84,10 @@ FindSCP
         this->_generator->initialize(request);
         while(!this->_generator->done())
         {
-            auto const data_set = this->_generator->get();
+            auto data_set = this->_generator->get();
             message::CFindResponse const response(
                 request.get_message_id(), message::CFindResponse::Pending,
-                data_set);
+                std::move(data_set));
             this->_association.send_message(
                 response, request.get_affected_sop_class_uid());
             this->_generator->next();
diff --git a/src/odil/FindSCP.h b/src/odil/FindSCP.h
index eb61994..7e416fc 100644
--- a/src/odil/FindSCP.h
+++ b/src/odil/FindSCP.h
@@ -12,14 +12,16 @@
 #include <memory>
 
 #include "odil/Association.h"
+#include "odil/odil.h"
 #include "odil/SCP.h"
+#include "odil/message/CFindRequest.h"
 #include "odil/message/Message.h"
 
 namespace odil
 {
 
 /// @brief SCP for C-Find services.
-class FindSCP: public SCP
+class ODIL_API FindSCP: public SCP
 {
 public:
 
@@ -43,8 +45,13 @@ public:
     /// @brief Process a C-Find request.
     virtual void operator()(message::Message const & message);
 
+    /// @brief Process a C-Find request.
+    virtual void operator()(message::Message && message);
+
 private:
     std::shared_ptr<DataSetGenerator> _generator;
+
+    void operator()(message::CFindRequest const & request);
 };
 
 }
diff --git a/src/odil/FindSCU.cpp b/src/odil/FindSCU.cpp
index 1c928ae..8b11681 100644
--- a/src/odil/FindSCU.cpp
+++ b/src/odil/FindSCU.cpp
@@ -10,11 +10,13 @@
 
 #include <functional>
 #include <sstream>
+#include <utility>
 #include <vector>
 
 #include "odil/Association.h"
 #include "odil/DataSet.h"
 #include "odil/Exception.h"
+#include "odil/logging.h"
 #include "odil/message/CFindRequest.h"
 #include "odil/message/CFindResponse.h"
 
@@ -41,6 +43,55 @@ FindSCU
     message::CFindRequest request(
         this->_association.next_message_id(),
         this->_affected_sop_class, message::Message::Priority::MEDIUM, query);
+    this->_find(request, callback);
+}
+
+void
+FindSCU
+::find(DataSet && query, Callback callback) const
+{
+    message::CFindRequest request(
+        this->_association.next_message_id(),
+        this->_affected_sop_class, message::Message::Priority::MEDIUM,
+        std::move(query));
+    this->_find(request, callback);
+}
+
+void _accumulate(std::vector<DataSet> & data_sets, DataSet && data_set)
+{
+    data_sets.push_back(std::move(data_set));
+}
+
+std::vector<DataSet>
+FindSCU
+::find(DataSet const & query) const
+{
+    std::vector<DataSet> result;
+    auto callback = [&result](DataSet && dataset) {
+        result.push_back(std::move(dataset));
+    };
+    this->find(query, callback);
+
+    return result;
+}
+
+std::vector<DataSet>
+FindSCU
+::find(DataSet && query) const
+{
+    std::vector<DataSet> result;
+    auto callback = [&result](DataSet && dataset) {
+        result.push_back(std::move(dataset));
+    };
+    this->find(std::move(query), callback);
+
+    return result;
+}
+
+void
+FindSCU
+::_find(message::CFindRequest const & request, Callback callback) const
+{
     this->_association.send_message(request, this->_affected_sop_class);
 
     // Receive the responses
@@ -48,7 +99,7 @@ FindSCU
     while(!done)
     {
         // FIXME: include progress callback
-        message::CFindResponse const response =
+        message::CFindResponse response =
             this->_association.receive_message();
 
         if(response.get_message_id_being_responded_to() != request.get_message_id())
@@ -69,25 +120,21 @@ FindSCU
             throw Exception(message.str());
         }
 
+        if(message::Response::is_warning(response.get_status()))
+        {
+            ODIL_LOG(WARN) << "C-FIND response status: " << response.get_status();
+        }
+        else if(message::Response::is_failure(response.get_status()))
+        {
+            ODIL_LOG(ERROR) << "C-FIND response status: " << response.get_status();
+        }
+
         done = !response.is_pending();
         if(!done)
         {
-            callback(response.get_data_set());
+            callback(std::move(response.get_data_set()));
         }
     }
 }
 
-std::vector<DataSet>
-FindSCU
-::find(DataSet const & query) const
-{
-    std::vector<DataSet> result;
-    auto callback = [&result](DataSet const & dataset) {
-        result.push_back(dataset);
-    };
-    this->find(query, callback);
-
-    return result;
-}
-
 }
diff --git a/src/odil/FindSCU.h b/src/odil/FindSCU.h
index 7bb6bd3..e209cf8 100644
--- a/src/odil/FindSCU.h
+++ b/src/odil/FindSCU.h
@@ -13,17 +13,19 @@
 
 #include "odil/Association.h"
 #include "odil/DataSet.h"
+#include "odil/message/CFindRequest.h"
+#include "odil/odil.h"
 #include "odil/SCU.h"
 
 namespace odil
 {
 
 /// @brief SCU for C-FIND services.
-class FindSCU: public SCU
+class ODIL_API FindSCU: public SCU
 {
 public:
     /// @brief Callback called when a response is received.
-    typedef std::function<void(DataSet const &)> Callback;
+    typedef std::function<void(DataSet &&)> Callback;
 
     /// @brief Constructor.
     FindSCU(Association & association);
@@ -33,12 +35,24 @@ public:
     
     /// @brief Perform the C-FIND using an optional callback.
     void find(DataSet const & query, Callback callback) const;
+
+    /// @brief Perform the C-FIND using an optional callback.
+    void find(DataSet && query, Callback callback) const;
     
     /**
      * @brief Return a list of datasets matching the query. The user is 
      * responsible for the de-allocation of the matches.
      */
     std::vector<DataSet> find(DataSet const & query) const;
+
+    /**
+     * @brief Return a list of datasets matching the query. The user is
+     * responsible for the de-allocation of the matches.
+     */
+    std::vector<DataSet> find(DataSet && query) const;
+
+private:
+    void _find(message::CFindRequest const & request, Callback callback) const;
 };
 
 }
diff --git a/src/odil/GetSCP.cpp b/src/odil/GetSCP.cpp
index 38508b9..875db32 100644
--- a/src/odil/GetSCP.cpp
+++ b/src/odil/GetSCP.cpp
@@ -62,7 +62,21 @@ GetSCP
 ::operator()(message::Message const & message)
 {
     message::CGetRequest const request(message);
+    this->operator()(request);
+}
 
+void
+GetSCP
+::operator()(message::Message && message)
+{
+    message::CGetRequest request(std::move(message));
+    this->operator()(request);
+}
+
+void
+GetSCP
+::operator()(message::CGetRequest const & request)
+{
     StoreSCU store_scu(this->_association);
 
     Value::Integer final_status = message::CGetResponse::Success;
@@ -92,11 +106,11 @@ GetSCP
             this->_association.send_message(
                 response, request.get_affected_sop_class_uid());
 
-            auto const data_set = this->_generator->get();
+            auto data_set = this->_generator->get();
             store_scu.set_affected_sop_class(data_set);
             try
             {
-                store_scu.store(data_set);
+                store_scu.store(std::move(data_set));
 
                 --remaining_sub_operations;
                 ++completed_sub_operations;
diff --git a/src/odil/GetSCP.h b/src/odil/GetSCP.h
index bdc8eb0..1da7d7e 100644
--- a/src/odil/GetSCP.h
+++ b/src/odil/GetSCP.h
@@ -12,14 +12,16 @@
 #include <memory>
 
 #include "odil/Association.h"
+#include "odil/odil.h"
 #include "odil/SCP.h"
+#include "odil/message/CGetRequest.h"
 #include "odil/message/Message.h"
 
 namespace odil
 {
 
 /// @brief SCP for C-Get services.
-class GetSCP: public SCP
+class ODIL_API GetSCP: public SCP
 {
 public:
 
@@ -51,8 +53,12 @@ public:
     /// @brief Process a C-Get request.
     virtual void operator()(message::Message const & message);
 
+    /// @brief Process a C-Get request.
+    virtual void operator()(message::Message && message);
+
 private:
     std::shared_ptr<DataSetGenerator> _generator;
+    void operator()(message::CGetRequest const & request);
 };
 
 }
diff --git a/src/odil/GetSCU.cpp b/src/odil/GetSCU.cpp
index 750a345..73bf329 100644
--- a/src/odil/GetSCU.cpp
+++ b/src/odil/GetSCU.cpp
@@ -15,6 +15,7 @@
 #include "odil/Association.h"
 #include "odil/DataSet.h"
 #include "odil/Exception.h"
+#include "odil/logging.h"
 #include "odil/StoreSCP.h"
 #include "odil/message/CGetRequest.h"
 #include "odil/message/CGetResponse.h"
@@ -46,25 +47,74 @@ GetSCU
     message::CGetRequest request(
         this->_association.next_message_id(),
         this->_affected_sop_class, message::Message::Priority::MEDIUM, query);
+    this->_get(request, store_callback, get_callback);
+}
+
+void
+GetSCU
+::get(
+    DataSet && query, StoreCallback store_callback,
+    GetCallback get_callback) const
+{
+    // Send the request
+    message::CGetRequest request(
+        this->_association.next_message_id(),
+        this->_affected_sop_class, message::Message::Priority::MEDIUM,
+        std::move(query));
+    this->_get(request, store_callback, get_callback);
+}
+
+std::vector<DataSet>
+GetSCU
+::get(DataSet const & query) const
+{
+    std::vector<DataSet> result;
+    auto callback = [&result](DataSet && data_set) {
+        result.push_back(data_set);
+    };
+    this->get(query, callback);
+
+    return result;
+}
+
+std::vector<DataSet>
+GetSCU
+::get(DataSet && query) const
+{
+    std::vector<DataSet> result;
+    auto callback = [&result](DataSet && data_set) {
+        result.push_back(std::move(data_set));
+    };
+    this->get(std::move(query), callback);
+
+    return result;
+}
+
+void
+GetSCU
+::_get(
+    message::CGetRequest const & request,
+    StoreCallback store_callback, GetCallback get_callback) const
+{
     this->_association.send_message(request, this->_affected_sop_class);
 
     // Receive the responses
     bool done = false;
     while(!done)
     {
-        message::Message const message = this->_association.receive_message();
+        message::Message message = this->_association.receive_message();
 
         if(message.get_command_field() == message::Message::Command::C_GET_RSP)
         {
             done = this->_handle_get_response(
-                message::CGetResponse(message), get_callback);
+                message::CGetResponse(std::move(message)), get_callback);
         }
         else if(message.get_command_field() == message::Message::Command::C_STORE_RQ)
         {
             try
             {
                 this->_handle_store_request(
-                    message::CStoreRequest(message), store_callback);
+                    message::CStoreRequest(std::move(message)), store_callback);
             }
             catch(...)
             {
@@ -82,45 +132,45 @@ GetSCU
     }
 }
 
-std::vector<DataSet>
-GetSCU
-::get(DataSet const & query) const
-{
-    std::vector<DataSet> result;
-    auto callback = [&result](DataSet const & data_set) {
-        result.push_back(data_set);
-    };
-    this->get(query, callback);
-
-    return result;
-}
-
 bool
 GetSCU
 ::_handle_get_response(
-    message::CGetResponse const & response, GetCallback callback) const
+    message::CGetResponse && response, GetCallback callback) const
 {
+    if(message::Response::is_warning(response.get_status()))
+    {
+        ODIL_LOG(WARN) << "C-GET response status: " << response.get_status();
+    }
+    else if(message::Response::is_failure(response.get_status()))
+    {
+        ODIL_LOG(ERROR) << "C-GET response status: " << response.get_status();
+    }
+
+    // Store status before moving the response.
+    auto const done = !response.is_pending();
+
     if(callback)
     {
-        callback(response);
+        callback(std::move(response));
     }
-    return !response.is_pending();
+
+    return done;
 }
 
 void
 GetSCU
 ::_handle_store_request(
-    message::CStoreRequest const & request, StoreCallback callback) const
+    message::CStoreRequest && request, StoreCallback callback) const
 {
-    auto const store_callback = [&callback](message::CStoreRequest const & request) {
+    auto const store_callback = [&callback](message::CStoreRequest && request) {
         if(callback)
         {
-            callback(request.get_data_set());
+            callback(std::move(request.get_data_set()));
         }
         return message::Response::Success;
     };
     StoreSCP scp(this->_association, store_callback);
-    scp(request);
+    scp(std::move(request));
 }
 
 }
diff --git a/src/odil/GetSCU.h b/src/odil/GetSCU.h
index 292f974..9b19088 100644
--- a/src/odil/GetSCU.h
+++ b/src/odil/GetSCU.h
@@ -14,7 +14,9 @@
 
 #include "odil/Association.h"
 #include "odil/DataSet.h"
+#include "odil/odil.h"
 #include "odil/SCU.h"
+#include "odil/message/CGetRequest.h"
 #include "odil/message/CGetResponse.h"
 #include "odil/message/CStoreRequest.h"
 
@@ -22,11 +24,11 @@ namespace odil
 {
 
 /// @brief SCU for C-GET services.
-class GetSCU: public SCU
+class ODIL_API GetSCU: public SCU
 {
 public:
     /// @brief Callback called when a C-STORE request is received.
-    typedef std::function<void(DataSet const &)> StoreCallback;
+    typedef std::function<void(DataSet &&)> StoreCallback;
 
     /**
      * @brief Typedef to keep compatibility with previous versions.
@@ -35,7 +37,7 @@ public:
     typedef StoreCallback Callback;
 
     /// @brief Callback called when a C-GET response is received.
-    typedef std::function<void(message::CGetResponse const &)> GetCallback;
+    typedef std::function<void(message::CGetResponse &&)> GetCallback;
 
     /// @brief Constructor.
     GetSCU(Association & association);
@@ -47,17 +49,30 @@ public:
     void get(
         DataSet const & query, StoreCallback store_callback,
         GetCallback get_callback=GetCallback()) const;
+
+    /// @brief Perform the C-GET using callbacks.
+    void get(
+        DataSet && query, StoreCallback store_callback,
+        GetCallback get_callback=GetCallback()) const;
     
     /**
      * @brief Return a list of datasets matching the query.
      */
     std::vector<DataSet> get(DataSet const & query) const;
 
+    /**
+     * @brief Return a list of datasets matching the query.
+     */
+    std::vector<DataSet> get(DataSet && query) const;
+
 private:
+    void _get(
+        message::CGetRequest const & request,
+        StoreCallback store_callback, GetCallback get_callback) const;
     bool _handle_get_response(
-        message::CGetResponse const & response, GetCallback callback) const;
+        message::CGetResponse && response, GetCallback callback) const;
     void _handle_store_request(
-        message::CStoreRequest const & request, StoreCallback callback) const;
+        message::CStoreRequest && request, StoreCallback callback) const;
 };
 
 }
diff --git a/src/odil/MoveSCP.cpp b/src/odil/MoveSCP.cpp
index 785492a..c52edc7 100644
--- a/src/odil/MoveSCP.cpp
+++ b/src/odil/MoveSCP.cpp
@@ -65,7 +65,21 @@ MoveSCP
 ::operator()(message::Message const & message)
 {
     message::CMoveRequest const request(message);
+    this->operator()(request);
+}
 
+void
+MoveSCP
+::operator()(message::Message && message)
+{
+    message::CMoveRequest const request(std::move(message));
+    this->operator()(request);
+}
+
+void
+MoveSCP
+::operator()(message::CMoveRequest const & request)
+{
     Association move_association;
     try
     {
@@ -114,11 +128,13 @@ MoveSCP
             this->_association.send_message(
                 response, request.get_affected_sop_class_uid());
 
-            auto const data_set = this->_generator->get();
+            auto data_set = this->_generator->get();
             store_scu.set_affected_sop_class(data_set);
             try
             {
-                store_scu.store(data_set, move_originator_aet, move_originator_message_id);
+                store_scu.store(
+                    std::move(data_set), move_originator_aet,
+                    move_originator_message_id);
 
                 --remaining_sub_operations;
                 ++completed_sub_operations;
diff --git a/src/odil/MoveSCP.h b/src/odil/MoveSCP.h
index 63004fd..b5bebe2 100644
--- a/src/odil/MoveSCP.h
+++ b/src/odil/MoveSCP.h
@@ -15,6 +15,7 @@
 #include <utility>
 
 #include "odil/Association.h"
+#include "odil/odil.h"
 #include "odil/SCP.h"
 #include "odil/message/CMoveRequest.h"
 #include "odil/message/Message.h"
@@ -23,7 +24,7 @@ namespace odil
 {
 
 /// @brief SCP for C-Move services.
-class MoveSCP: public SCP
+class ODIL_API MoveSCP: public SCP
 {
 public:
 
@@ -63,8 +64,12 @@ public:
     /// @brief Process a C-Get request.
     virtual void operator()(message::Message const & message);
 
+    /// @brief Process a C-Get request.
+    virtual void operator()(message::Message && message);
+
 private:
     std::shared_ptr<DataSetGenerator> _generator;
+    void operator()(message::CMoveRequest const & request);
 };
 
 }
diff --git a/src/odil/MoveSCU.cpp b/src/odil/MoveSCU.cpp
index f39a151..64528a1 100644
--- a/src/odil/MoveSCU.cpp
+++ b/src/odil/MoveSCU.cpp
@@ -16,6 +16,7 @@
 #include "odil/Association.h"
 #include "odil/DataSet.h"
 #include "odil/Exception.h"
+#include "odil/logging.h"
 #include "odil/StoreSCP.h"
 #include "odil/message/CMoveRequest.h"
 #include "odil/message/CMoveResponse.h"
@@ -74,11 +75,24 @@ MoveSCU
 
 void
 MoveSCU
+::move(DataSet && query, StoreCallback store_callback) const
+{
+    this->move(std::move(query), store_callback, MoveCallback());
+}
+
+void
+MoveSCU
 ::move(DataSet const & query, MoveCallback move_callback) const
 {
     this->move(query, StoreCallback(), move_callback);
 }
 
+void
+MoveSCU
+::move(DataSet && query, MoveCallback move_callback) const
+{
+    this->move(std::move(query), StoreCallback(), move_callback);
+}
 
 void
 MoveSCU
@@ -91,6 +105,29 @@ MoveSCU
         this->_association.next_message_id(),
         this->_affected_sop_class, message::Message::Priority::MEDIUM,
         this->_move_destination, query);
+    this->_move(request, store_callback, move_callback);
+}
+
+void
+MoveSCU
+::move(
+    DataSet && query, StoreCallback store_callback,
+    MoveCallback move_callback) const
+{
+    // Send the request
+    message::CMoveRequest const request(
+        this->_association.next_message_id(),
+        this->_affected_sop_class, message::Message::Priority::MEDIUM,
+        this->_move_destination, std::move(query));
+    this->_move(request, store_callback, move_callback);
+}
+
+void
+MoveSCU
+::_move(
+    message::CMoveRequest const & request, StoreCallback store_callback,
+    MoveCallback move_callback) const
+{
     this->_association.send_message(request, this->_affected_sop_class);
 
     // Receive the responses
@@ -145,6 +182,19 @@ MoveSCU
     return result;
 }
 
+std::vector<DataSet>
+MoveSCU
+::move(DataSet && query) const
+{
+    std::vector<DataSet> result;
+    auto callback = [&result](DataSet && data_set) {
+        result.push_back(std::move(data_set));
+    };
+    this->move(std::move(query), callback, MoveCallback());
+
+    return result;
+}
+
 void
 MoveSCU
 ::_dispatch(
@@ -174,12 +224,24 @@ bool
 MoveSCU
 ::_handle_main_association(MoveCallback callback) const
 {
-    message::CMoveResponse const response = this->_association.receive_message();
+    message::CMoveResponse response = this->_association.receive_message();
+    if(message::Response::is_warning(response.get_status()))
+    {
+        ODIL_LOG(WARN) << "C-MOVE response status: " << response.get_status();
+    }
+    else if(message::Response::is_failure(response.get_status()))
+    {
+        ODIL_LOG(ERROR) << "C-MOVE response status: " << response.get_status();
+    }
+
+    // Store status before moving the response.
+    auto const done = !response.is_pending();
+
     if(callback)
     {
-        callback(response);
+        callback(std::move(response));
     }
-    return !response.is_pending();
+    return done;
 }
 
 bool
@@ -190,10 +252,10 @@ MoveSCU
     bool result = false;
     try
     {
-        auto const store_callback = [&callback](message::CStoreRequest const & request) {
+        auto const store_callback = [&callback](message::CStoreRequest && request) {
             if(callback)
             {
-                callback(request.get_data_set());
+                callback(std::move(request.get_data_set()));
             }
             return message::Response::Success;
         };
diff --git a/src/odil/MoveSCU.h b/src/odil/MoveSCU.h
index 76be7f5..e4f3b40 100644
--- a/src/odil/MoveSCU.h
+++ b/src/odil/MoveSCU.h
@@ -15,18 +15,20 @@
 
 #include "odil/Association.h"
 #include "odil/DataSet.h"
+#include "odil/message/CMoveRequest.h"
 #include "odil/message/CMoveResponse.h"
+#include "odil/odil.h"
 #include "odil/SCU.h"
 
 namespace odil
 {
 
 /// @brief SCU for C-MOVE services.
-class MoveSCU: public SCU
+class ODIL_API MoveSCU: public SCU
 {
 public:
     /// @brief Callback called when a C-STORE request is received.
-    typedef std::function<void(DataSet const &)> StoreCallback;
+    typedef std::function<void(DataSet &&)> StoreCallback;
 
     /**
      * @brief Typedef to keep compatibility with previous versions.
@@ -35,7 +37,7 @@ public:
     typedef StoreCallback Callback;
 
     /// @brief Callback called when a C-MOVE response is received.
-    typedef std::function<void(message::CMoveResponse const &)> MoveCallback;
+    typedef std::function<void(message::CMoveResponse &&)> MoveCallback;
     
     /// @brief Constructor.
     MoveSCU(Association & association);
@@ -56,23 +58,43 @@ public:
     
     /// @brief Perform the C-MOVE using callbacks.
     void move(DataSet const & query, StoreCallback store_callback) const;
+
+    /// @brief Perform the C-MOVE using callbacks.
+    void move(DataSet && query, StoreCallback store_callback) const;
         
     /// @brief Perform the C-MOVE using callbacks.
     void move(DataSet const & query, MoveCallback move_callback) const;
 
     /// @brief Perform the C-MOVE using callbacks.
+    void move(DataSet && query, MoveCallback move_callback) const;
+
+    /// @brief Perform the C-MOVE using callbacks.
     void move(
         DataSet const & query, StoreCallback store_callback,
         MoveCallback move_callback) const;
+
+    /// @brief Perform the C-MOVE using callbacks.
+    void move(
+        DataSet && query, StoreCallback store_callback,
+        MoveCallback move_callback) const;
     
     /**
      * @brief Return a list of datasets matching the query.
      */
     std::vector<DataSet> move(DataSet const & query) const;
 
+    /**
+     * @brief Return a list of datasets matching the query.
+     */
+    std::vector<DataSet> move(DataSet && query) const;
+
 private:
     std::string _move_destination;
     uint16_t _incoming_port;
+
+    void _move(
+        message::CMoveRequest const & request, StoreCallback store_callback,
+        MoveCallback move_callback) const;
     
     void _dispatch(
         Association & store_association, StoreCallback store_callback,
diff --git a/src/odil/StoreSCP.cpp b/src/odil/NCreateSCP.cpp
similarity index 58%
copy from src/odil/StoreSCP.cpp
copy to src/odil/NCreateSCP.cpp
index ae1a3a9..67004e8 100644
--- a/src/odil/StoreSCP.cpp
+++ b/src/odil/NCreateSCP.cpp
@@ -6,63 +6,72 @@
  * for details.
  ************************************************************************/
 
-#include "odil/StoreSCP.h"
+#include "odil/NCreateSCP.h"
 
 #include <functional>
 
 #include "odil/Association.h"
-#include "odil/Exception.h"
 #include "odil/SCP.h"
 #include "odil/Value.h"
-#include "odil/message/CStoreRequest.h"
-#include "odil/message/CStoreResponse.h"
-#include "odil/message/CStoreRequest.h"
-#include "odil/message/CStoreResponse.h"
+#include "odil/message/NCreateResponse.h"
 
 namespace odil
 {
 
-StoreSCP
-::StoreSCP(Association & association)
+NCreateSCP
+::NCreateSCP(Association & association)
 : SCP(association), _callback()
 {
     // Nothing else.
 }
 
-StoreSCP
-::StoreSCP(Association & association, Callback const & callback)
+NCreateSCP
+::NCreateSCP(Association & association, Callback const & callback)
 : SCP(association), _callback()
 {
     this->set_callback(callback);
 }
 
-StoreSCP
-::~StoreSCP()
+NCreateSCP
+::~NCreateSCP()
 {
     // Nothing to do.
 }
 
-StoreSCP::Callback const &
-StoreSCP
-::get_callback() const
+NCreateSCP::Callback const &
+NCreateSCP::get_callback() const
 {
     return this->_callback;
 }
 
 void
-StoreSCP
+NCreateSCP
 ::set_callback(Callback const & callback)
 {
     this->_callback = callback;
 }
 
 void
-StoreSCP
+NCreateSCP
 ::operator()(message::Message const & message)
 {
-    message::CStoreRequest const request(message);
+    message::NCreateRequest const request(message);
+    this->operator()(request);
+}
 
-    Value::Integer status=message::CStoreResponse::Success;
+void
+NCreateSCP
+::operator()(message::Message && message)
+{
+    message::NCreateRequest const request(std::move(message));
+    this->operator()(request);
+}
+
+void
+NCreateSCP
+::operator()(message::NCreateRequest const & request)
+{
+    Value::Integer status=message::NCreateResponse::Success;
     DataSet status_fields;
 
     try
@@ -76,10 +85,12 @@ StoreSCP
     }
     catch(odil::Exception const &)
     {
-        status = message::CStoreResponse::ProcessingFailure;
+        status = message::NCreateResponse::ProcessingFailure;
     }
 
-    message::CStoreResponse response(request.get_message_id(), status);
+    message::NCreateResponse response(
+        request.get_message_id()
+                , status, request.get_affected_sop_class_uid());
     response.set_status_fields(status_fields);
     this->_association.send_message(
         response, request.get_affected_sop_class_uid());
diff --git a/src/odil/EchoSCP.h b/src/odil/NCreateSCP.h
similarity index 59%
copy from src/odil/EchoSCP.h
copy to src/odil/NCreateSCP.h
index 5fa0433..20940fb 100644
--- a/src/odil/EchoSCP.h
+++ b/src/odil/NCreateSCP.h
@@ -6,38 +6,39 @@
  * for details.
  ************************************************************************/
 
-#ifndef _1a61d976_ba12_4dba_af34_67f064d38506
-#define _1a61d976_ba12_4dba_af34_67f064d38506
+#ifndef _918ce553_d774_44c0_9cbf_56f32584a1ab
+#define _918ce553_d774_44c0_9cbf_56f32584a1ab
 
 #include <functional>
 
 #include "odil/Association.h"
+#include "odil/odil.h"
 #include "odil/SCP.h"
 #include "odil/Value.h"
-#include "odil/message/CEchoRequest.h"
+#include "odil/message/NCreateRequest.h"
 #include "odil/message/Message.h"
 
 namespace odil
 {
 
-/// @brief SCP for C-Echo services.
-class EchoSCP: public SCP
+/// @brief SCP for N-Create services.
+class ODIL_API NCreateSCP: public SCP
 {
 public:
     /**
      * @brief Callback called when a request is received, shall throw an
      * SCP::Exception on error.
      */
-    typedef std::function<Value::Integer(message::CEchoRequest const &)> Callback;
+    typedef std::function<Value::Integer(message::NCreateRequest const &)> Callback;
 
     /// @brief Constructor.
-    EchoSCP(Association & association);
+    NCreateSCP(Association & association);
 
     /// @brief Constructor.
-    EchoSCP(Association & association, Callback const & callback);
+    NCreateSCP(Association & association, Callback const & callback);
 
     /// @brief Destructor.
-    virtual ~EchoSCP();
+    virtual ~NCreateSCP();
 
     /// @brief Return the callback.
     Callback const & get_callback() const;
@@ -45,13 +46,17 @@ public:
     /// @brief Set the callback.
     void set_callback(Callback const & callback);
 
-    /// @brief Process a C-Echo request.
+    /// @brief Process a N-Create request.
     virtual void operator()(message::Message const & message);
 
+    /// @brief Process a N-Create request.
+    virtual void operator()(message::Message && message);
+
 private:
     Callback _callback;
+    void operator()(message::NCreateRequest const & message);
 };
 
 }
 
-#endif // _1a61d976_ba12_4dba_af34_67f064d38506
+#endif // _918ce553_d774_44c0_9cbf_56f32584a1ab
diff --git a/src/odil/NSetSCP.cpp b/src/odil/NSetSCP.cpp
new file mode 100644
index 0000000..5bce0d2
--- /dev/null
+++ b/src/odil/NSetSCP.cpp
@@ -0,0 +1,102 @@
+/*************************************************************************
+ * odil - Copyright (C) Universite de Strasbourg
+ * Distributed under the terms of the CeCILL-B license, as published by
+ * the CEA-CNRS-INRIA. Refer to the LICENSE file or to
+ * http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html
+ * for details.
+ ************************************************************************/
+
+#include "odil/Association.h"
+#include "odil/NSetSCP.h"
+#include "odil/SCP.h"
+#include "odil/Value.h"
+#include "odil/message/NSetResponse.h"
+
+#include <functional>
+
+namespace odil
+{
+
+NSetSCP
+::NSetSCP(Association& association)
+    : SCP(association), _callback()
+{
+    // Nothing else.
+}
+
+NSetSCP
+::NSetSCP(Association& association, Callback const& callback)
+    : SCP(association), _callback()
+{
+    this->set_callback(callback);
+}
+
+NSetSCP
+::~NSetSCP()
+{
+    // Nothing to do.
+}
+
+NSetSCP::Callback const&
+NSetSCP::get_callback() const
+{
+    return this->_callback;
+}
+
+void
+NSetSCP
+::set_callback(Callback const& callback)
+{
+    this->_callback = callback;
+}
+
+void
+NSetSCP
+::operator()(message::Message const & message)
+{
+    message::NSetRequest const request(message);
+    this->operator()(request);
+}
+
+void
+NSetSCP
+::operator()(message::Message && message)
+{
+    message::NSetRequest const request(std::move(message));
+    this->operator()(request);
+}
+
+void
+NSetSCP
+::operator()(message::NSetRequest const & request)
+{
+    Value::Integer status = message::NSetResponse::Success;
+    DataSet status_fields;
+
+    try
+    {
+        status = this->_callback(request);
+    }
+    catch(SCP::Exception const& e)
+    {
+        status        = e.status;
+        status_fields = e.status_fields;
+    }
+    catch(odil::Exception const&)
+    {
+        status = message::NSetResponse::ProcessingFailure;
+    }
+
+    message::NSetResponse response(
+        request.get_message_id(),
+        status,
+        request.get_requested_sop_class_uid(),
+        request.get_requested_sop_instance_uid() );
+
+    response.set_status_fields(status_fields);
+
+    this->_association.send_message(
+        response, request.get_requested_sop_class_uid());
+}
+
+}
diff --git a/src/odil/EchoSCP.h b/src/odil/NSetSCP.h
similarity index 60%
copy from src/odil/EchoSCP.h
copy to src/odil/NSetSCP.h
index 5fa0433..4c22784 100644
--- a/src/odil/EchoSCP.h
+++ b/src/odil/NSetSCP.h
@@ -6,38 +6,39 @@
  * for details.
  ************************************************************************/
 
-#ifndef _1a61d976_ba12_4dba_af34_67f064d38506
-#define _1a61d976_ba12_4dba_af34_67f064d38506
+#ifndef _cca2fb7a_c3b6_47f4_a619_44701b074cda
+#define _cca2fb7a_c3b6_47f4_a619_44701b074cda
 
 #include <functional>
 
 #include "odil/Association.h"
+#include "odil/odil.h"
 #include "odil/SCP.h"
 #include "odil/Value.h"
-#include "odil/message/CEchoRequest.h"
 #include "odil/message/Message.h"
+#include "odil/message/NSetRequest.h"
 
 namespace odil
 {
 
-/// @brief SCP for C-Echo services.
-class EchoSCP: public SCP
+/// @brief SCP for N-Set services.
+class ODIL_API NSetSCP: public SCP
 {
 public:
     /**
      * @brief Callback called when a request is received, shall throw an
      * SCP::Exception on error.
      */
-    typedef std::function<Value::Integer(message::CEchoRequest const &)> Callback;
+    typedef std::function<Value::Integer(message::NSetRequest const &)> Callback;
 
     /// @brief Constructor.
-    EchoSCP(Association & association);
+    NSetSCP(Association & association);
 
     /// @brief Constructor.
-    EchoSCP(Association & association, Callback const & callback);
+    NSetSCP(Association & association, Callback const & callback);
 
     /// @brief Destructor.
-    virtual ~EchoSCP();
+    virtual ~NSetSCP();
 
     /// @brief Return the callback.
     Callback const & get_callback() const;
@@ -45,13 +46,18 @@ public:
     /// @brief Set the callback.
     void set_callback(Callback const & callback);
 
-    /// @brief Process a C-Echo request.
+    /// @brief Process a N-Set request.
     virtual void operator()(message::Message const & message);
 
+    /// @brief Process a N-Set request.
+    virtual void operator()(message::Message && message);
+
+
 private:
     Callback _callback;
+    void operator()(message::NSetRequest const & message);
 };
 
 }
 
-#endif // _1a61d976_ba12_4dba_af34_67f064d38506
+#endif // _cca2fb7a_c3b6_47f4_a619_44701b074cda
diff --git a/src/odil/NSetSCU.cpp b/src/odil/NSetSCU.cpp
new file mode 100644
index 0000000..5f308d5
--- /dev/null
+++ b/src/odil/NSetSCU.cpp
@@ -0,0 +1,74 @@
+/*************************************************************************
+ * odil - Copyright (C) Universite de Strasbourg
+ * Distributed under the terms of the CeCILL-B license, as published by
+ * the CEA-CNRS-INRIA. Refer to the LICENSE file or to
+ * http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html
+ * for details.
+ ************************************************************************/
+
+#include "NSetSCU.h"
+
+#include <algorithm>
+#include <sstream>
+#include <string>
+#include <vector>
+
+#include "odil/message/NSetRequest.h"
+#include "odil/message/NSetResponse.h"
+#include "odil/DataSet.h"
+#include "odil/Exception.h"
+#include "odil/registry.h"
+#include "odil/SCU.h"
+
+namespace odil
+{
+
+NSetSCU
+::NSetSCU(Association & association)
+: SCU(association)
+{
+    // Nothing else.
+}
+
+NSetSCU
+::~NSetSCU()
+{
+    // Nothing to do.
+}
+
+void 
+NSetSCU
+::set_affected_sop_class(DataSet const & dataset)
+{
+    auto const & sop_class_uid = dataset.as_string(registry::RequestedSOPClassUID, 0);
+
+    this->SCU::set_affected_sop_class(sop_class_uid);
+}
+
+void 
+NSetSCU
+::set( DataSet const & dataset) const
+{
+
+    message::NSetRequest const request(
+        this->_association.next_message_id(),
+        this->_affected_sop_class,
+        dataset.as_string(registry::RequestedSOPInstanceUID, 0),
+        dataset
+        );
+
+    this->_association.send_message(request, this->_affected_sop_class);
+    
+    message::NSetResponse const response = this->_association.receive_message();
+
+    if(response.get_message_id_being_responded_to() != request.get_message_id())
+    {
+        std::ostringstream message;
+        message << "DIMSE: Unexpected Response MsgId: "
+                << response.get_message_id_being_responded_to()
+                << "(expected: " << request.get_message_id() << ")";
+        throw Exception(message.str());
+    }
+}
+
+}
diff --git a/src/odil/StoreSCU.h b/src/odil/NSetSCU.h
similarity index 63%
copy from src/odil/StoreSCU.h
copy to src/odil/NSetSCU.h
index 7500fa1..be67ae0 100644
--- a/src/odil/StoreSCU.h
+++ b/src/odil/NSetSCU.h
@@ -6,38 +6,36 @@
  * for details.
  ************************************************************************/
 
-#ifndef _1b2f876e_1ad2_464d_9423_28181320aed0
-#define _1b2f876e_1ad2_464d_9423_28181320aed0
+#ifndef _c80c338c_36d7_4724_9732_c7afed87902b
+#define _c80c338c_36d7_4724_9732_c7afed87902b
 
 #include "odil/Association.h"
 #include "odil/DataSet.h"
+#include "odil/odil.h"
 #include "odil/SCU.h"
 
 namespace odil
 {
 
 /// @brief SCU for C-Store services.
-class StoreSCU: public SCU
+class ODIL_API NSetSCU: public SCU
 {
 public:
     /// @brief Constructor.
-    StoreSCU(Association & association);
+    NSetSCU(Association & association);
 
     /// @brief Destructor.
-    virtual ~StoreSCU();
+    virtual ~NSetSCU();
     
     /// @brief Set the affected SOP class based on the dataset.
     void set_affected_sop_class(DataSet const & dataset);
 
 	using SCU::set_affected_sop_class;
     
-    /// @brief Perform the C-STORE.
-    void store(
-        DataSet const & dataset,
-        Value::String const & move_originator_ae_title = "",
-        Value::Integer move_originator_message_id = -1) const;
+    /// @brief Perform the N-SET.
+    void set( DataSet const & dataset) const;
 };
 
 }
 
-#endif // _1b2f876e_1ad2_464d_9423_28181320aed0
+#endif // _c80c338c_36d7_4724_9732_c7afed87902b
diff --git a/src/odil/Reader.cpp b/src/odil/Reader.cpp
index 6c06033..587d905 100644
--- a/src/odil/Reader.cpp
+++ b/src/odil/Reader.cpp
@@ -12,6 +12,7 @@
 #include <cstdint>
 #include <functional>
 #include <istream>
+#include <memory>
 #include <sstream>
 #include <string>
 #include <utility>
@@ -141,7 +142,7 @@ Reader
 
             if(this->keep_group_length || tag.element != 0)
             {
-                data_set.add(tag, element);
+                data_set.add(tag, std::move(element));
             }
         }
 
@@ -210,56 +211,43 @@ Reader
         vr = vr_finder(tag, data_set, this->transfer_syntax);
     }
 
-    Value value;
+    std::shared_ptr<Value> value;
 
     auto const vl = this->read_length(vr);
-    if(vl == 0)
+    if(is_int(vr))
     {
-        value = Value();
+        value = std::make_shared<Value>(Value::Integers());
     }
-    else if(vr == VR::IS || vr == VR::SL || vr == VR::SS ||
-        vr == VR::UL || vr == VR::US)
+    else if(is_real(vr))
     {
-        value = Value(Value::Integers());
+        value = std::make_shared<Value>(Value::Reals());
     }
-    else if(vr == VR::DS || vr == VR::FD || vr == VR::FL)
+    else if(is_string(vr))
     {
-        value = Value(Value::Reals());
-    }
-    else if(vr == VR::AE || vr == VR::AS || vr == VR::AT || vr == VR::CS ||
-        vr == VR::DA || vr == VR::DS || vr == VR::DT || vr == VR::LO ||
-        vr == VR::LT || vr == VR::PN || vr == VR::SH || vr == VR::ST ||
-        vr == VR::TM || vr == VR::UC || vr == VR::UI || vr == VR::UR ||
-        vr == VR::UT)
-    {
-        value = Value(Value::Strings());
+        value = std::make_shared<Value>(Value::Strings());
     }
     else if(vr == VR::SQ)
     {
-        value = Value(Value::DataSets());
+        value = std::make_shared<Value>(Value::DataSets());
     }
     else if(is_binary(vr))
     {
-        value = Value(Value::Binary());
+        value = std::make_shared<Value>(Value::Binary());
     }
     else
     {
         throw Exception("Cannot create value for VR " + as_string(vr));
     }
 
-    if(!value.empty())
+    if(vl > 0)
     {
         Visitor visitor(
             this->stream, vr, vl, this->transfer_syntax, this->byte_ordering,
             this->explicit_vr, this->keep_group_length);
-        apply_visitor(visitor, value);
-        if(vr == VR::SQ && value.as_data_sets().empty())
-        {
-            value = Value();
-        }
+        apply_visitor(visitor, *value);
     }
 
-    return Element(value, vr);
+    return Element(*value, vr);
 }
 
 std::pair<DataSet, DataSet>
@@ -285,7 +273,7 @@ Reader
     // Read meta information
     Reader meta_information_reader(
         stream, registry::ExplicitVRLittleEndian, keep_group_length);
-    auto const meta_information = meta_information_reader.read_data_set(
+    auto meta_information = meta_information_reader.read_data_set(
         [](Tag const & tag) { return (tag.group != 0x0002); });
 
     if(!meta_information.has(registry::TransferSyntaxUID))
@@ -304,9 +292,9 @@ Reader
     Reader data_set_reader(
         stream, meta_information.as_string(registry::TransferSyntaxUID)[0],
         keep_group_length);
-    auto const data_set = data_set_reader.read_data_set(halt_condition);
+    auto data_set = data_set_reader.read_data_set(halt_condition);
 
-    return std::pair<DataSet, DataSet>(meta_information, data_set);
+    return std::make_pair(std::move(meta_information), std::move(data_set));
 }
 
 Reader::Visitor
diff --git a/src/odil/Reader.h b/src/odil/Reader.h
index e4193ce..f36f00f 100644
--- a/src/odil/Reader.h
+++ b/src/odil/Reader.h
@@ -17,6 +17,7 @@
 #include "odil/DataSet.h"
 #include "odil/Element.h"
 #include "odil/endian.h"
+#include "odil/odil.h"
 #include "odil/Tag.h"
 #include "odil/Value.h"
 #include "odil/VR.h"
@@ -25,7 +26,7 @@ namespace odil
 {
 
 /// @brief Read DICOM objects from a stream.
-class Reader
+class ODIL_API Reader
 {
 public:
     /// @brief Input stream.
diff --git a/src/odil/SCP.cpp b/src/odil/SCP.cpp
index 854c0b6..422d55f 100644
--- a/src/odil/SCP.cpp
+++ b/src/odil/SCP.cpp
@@ -54,8 +54,8 @@ void
 SCP
 ::receive_and_process()
 {
-    auto const message = this->_association.receive_message();
-    (*this)(message);
+    auto message = this->_association.receive_message();
+    (*this)(std::move(message));
 }
 
 }
diff --git a/src/odil/SCP.h b/src/odil/SCP.h
index 4f55077..2a2dc1a 100644
--- a/src/odil/SCP.h
+++ b/src/odil/SCP.h
@@ -14,13 +14,14 @@
 #include "odil/Exception.h"
 #include "odil/message/Message.h"
 #include "odil/message/Request.h"
+#include "odil/odil.h"
 #include "odil/Value.h"
 
 namespace odil
 {
 
 /// @brief Base class for all Service Class Providers.
-class SCP
+class ODIL_API SCP
 {
 public:
     /**
@@ -28,7 +29,7 @@ public:
      *
      * initialize, done, next and get shall throw an SCP::Exception on error.
      */
-    class DataSetGenerator
+    class ODIL_API DataSetGenerator
     {
     public:
         /// @brief Destructor.
@@ -76,6 +77,9 @@ public:
 
     /// @brief Process a message.
     virtual void operator()(message::Message const & message) =0;
+
+    /// @brief Process a message.
+    virtual void operator()(message::Message && message) =0;
 protected:
     /// @brief Association with peer.
     Association & _association;
diff --git a/src/odil/SCPDispatcher.cpp b/src/odil/SCPDispatcher.cpp
index 6ddcbdc..a0828bc 100644
--- a/src/odil/SCPDispatcher.cpp
+++ b/src/odil/SCPDispatcher.cpp
@@ -6,22 +6,22 @@
  * for details.
  ************************************************************************/
 
-#include "odil/SCPDispatcher.h"
-
-#include <map>
-#include <memory>
-
 #include "odil/Association.h"
 #include "odil/Exception.h"
 #include "odil/SCP.h"
+#include "odil/SCPDispatcher.h"
 #include "odil/Value.h"
 
+#include <map>
+#include <memory>
+#include <sstream>
+
 namespace odil
 {
 
 SCPDispatcher
-::SCPDispatcher(Association & association)
-: _association(association)
+::SCPDispatcher(Association& association)
+    : _association(association)
 {
     // Nothing else.
 }
@@ -40,7 +40,7 @@ SCPDispatcher
     return (it != this->_providers.end());
 }
 
-std::shared_ptr<SCP> const &
+std::shared_ptr<SCP> const&
 SCPDispatcher
 ::get_scp(Value::Integer command) const
 {
@@ -54,7 +54,7 @@ SCPDispatcher
 
 void
 SCPDispatcher
-::set_scp(Value::Integer command, std::shared_ptr<SCP> const & scp)
+::set_scp(Value::Integer command, std::shared_ptr<SCP> const& scp)
 {
     this->_providers[command] = scp;
 }
@@ -68,10 +68,15 @@ SCPDispatcher
     auto const it = this->_providers.find(message.get_command_field());
     if(it == this->_providers.end())
     {
-        throw Exception("No such provider");
+        std::ostringstream error_message;
+        error_message
+            << "No provider for: "
+            << std::hex << std::setw(4) << std::setfill('0')
+            << message.get_command_field();
+        throw Exception(error_message.str());
     }
 
-    auto & scp = *(it->second);
+    auto& scp = *(it->second);
     scp(message);
 }
 
diff --git a/src/odil/SCPDispatcher.h b/src/odil/SCPDispatcher.h
index 511b19f..f638dc7 100644
--- a/src/odil/SCPDispatcher.h
+++ b/src/odil/SCPDispatcher.h
@@ -13,6 +13,7 @@
 #include <memory>
 
 #include "odil/Association.h"
+#include "odil/odil.h"
 #include "odil/SCP.h"
 #include "odil/Value.h"
 
@@ -20,7 +21,7 @@ namespace odil
 {
 
 /// @brief Dispatch an incoming message to one of the registered SCPs.
-class SCPDispatcher
+class ODIL_API SCPDispatcher
 {
 public:
     /// @brief Create a dispatcher with network and association.
@@ -40,11 +41,13 @@ public:
 
     /// @brief Receive and dispatch an incoming message.
     void dispatch();
+
 private:
     typedef std::shared_ptr<SCP> SCPPointer;
 
     Association & _association;
-    std::map<Value::Integer, std::shared_ptr<SCP>> _providers;
+    std::map<Value::Integer, std::shared_ptr<SCP> > _providers;
+
 };
 
 }
diff --git a/src/odil/SCU.h b/src/odil/SCU.h
index 0f380bd..7c46903 100644
--- a/src/odil/SCU.h
+++ b/src/odil/SCU.h
@@ -12,12 +12,13 @@
 #include <string>
 
 #include "odil/Association.h"
+#include "odil/odil.h"
 
 namespace odil
 {
 
 /// @brief Base class for all Service Class Users.
-class SCU
+class ODIL_API SCU
 {
 public:
     /// @brief Create a default Service Class User.
diff --git a/src/odil/StoreSCP.cpp b/src/odil/StoreSCP.cpp
index ae1a3a9..3224aa2 100644
--- a/src/odil/StoreSCP.cpp
+++ b/src/odil/StoreSCP.cpp
@@ -60,14 +60,28 @@ void
 StoreSCP
 ::operator()(message::Message const & message)
 {
-    message::CStoreRequest const request(message);
+    message::CStoreRequest request(message);
+    this->operator()(request);
+}
 
+void
+StoreSCP
+::operator()(message::Message && message)
+{
+    message::CStoreRequest request(std::move(message));
+    this->operator()(request);
+}
+
+void
+StoreSCP
+::operator()(message::CStoreRequest & request)
+{
     Value::Integer status=message::CStoreResponse::Success;
     DataSet status_fields;
 
     try
     {
-        status = this->_callback(request);
+        status = this->_callback(std::move(request));
     }
     catch(SCP::Exception const & e)
     {
diff --git a/src/odil/StoreSCP.h b/src/odil/StoreSCP.h
index 55ac977..c1e0703 100644
--- a/src/odil/StoreSCP.h
+++ b/src/odil/StoreSCP.h
@@ -12,6 +12,7 @@
 #include <functional>
 
 #include "odil/Association.h"
+#include "odil/odil.h"
 #include "odil/SCP.h"
 #include "odil/Value.h"
 #include "odil/message/CStoreRequest.h"
@@ -21,14 +22,14 @@ namespace odil
 {
 
 /// @brief SCP for C-Store services.
-class StoreSCP: public SCP
+class ODIL_API StoreSCP: public SCP
 {
 public:
     /**
      * @brief Callback called when a request is received, shall throw an
      * SCP::Exception on error.
      */
-    typedef std::function<Value::Integer(message::CStoreRequest const &)> Callback;
+    typedef std::function<Value::Integer(message::CStoreRequest &&)> Callback;
 
     /// @brief Constructor.
     StoreSCP(Association & association);
@@ -48,8 +49,12 @@ public:
     /// @brief Process a C-Store request.
     virtual void operator()(message::Message const & message);
 
+    /// @brief Process a C-Store request.
+    virtual void operator()(message::Message && message);
+
 private:
     Callback _callback;
+    void operator()(message::CStoreRequest & request);
 };
 
 }
diff --git a/src/odil/StoreSCU.cpp b/src/odil/StoreSCU.cpp
index f4c7ae7..876499d 100644
--- a/src/odil/StoreSCU.cpp
+++ b/src/odil/StoreSCU.cpp
@@ -17,6 +17,7 @@
 #include "odil/message/CStoreResponse.h"
 #include "odil/DataSet.h"
 #include "odil/Exception.h"
+#include "odil/logging.h"
 #include "odil/registry.h"
 #include "odil/SCU.h"
 
@@ -66,8 +67,8 @@ void
 StoreSCU
 ::store(
     DataSet const & dataset,
-    Value::String const & move_originator_ae_title,
-    Value::Integer move_originator_message_id) const
+    Value::String const & move_originator_ae_title ,
+    Value::Integer move_originator_message_id ) const
 {
     message::CStoreRequest const request(
         this->_association.next_message_id(),
@@ -76,7 +77,30 @@ StoreSCU
         message::Message::Priority::MEDIUM,
         dataset, move_originator_ae_title,
         move_originator_message_id);
+    this->_store(request);
+}
 
+void
+StoreSCU
+::store(
+    DataSet && dataset,
+    Value::String const & move_originator_ae_title ,
+    Value::Integer move_originator_message_id ) const
+{
+    message::CStoreRequest const request(
+        this->_association.next_message_id(),
+        this->_affected_sop_class,
+        dataset.as_string(registry::SOPInstanceUID, 0),
+        message::Message::Priority::MEDIUM,
+        std::move(dataset), move_originator_ae_title,
+        move_originator_message_id);
+    this->_store(request);
+}
+
+void
+StoreSCU
+::_store(message::CStoreRequest const & request) const
+{
     this->_association.send_message(request, this->_affected_sop_class);
     
     message::CStoreResponse const response = this->_association.receive_message();
@@ -108,6 +132,15 @@ StoreSCU
                 << " (expected: " << request.get_affected_sop_instance_uid() << ")";
         throw Exception(message.str());
     }
+
+    if(message::Response::is_warning(response.get_status()))
+    {
+        ODIL_LOG(WARN) << "C-STORE response status: " << response.get_status();
+    }
+    else if(message::Response::is_failure(response.get_status()))
+    {
+        ODIL_LOG(ERROR) << "C-STORE response status: " << response.get_status();
+    }
 }
 
 }
diff --git a/src/odil/StoreSCU.h b/src/odil/StoreSCU.h
index 7500fa1..80213c8 100644
--- a/src/odil/StoreSCU.h
+++ b/src/odil/StoreSCU.h
@@ -11,13 +11,15 @@
 
 #include "odil/Association.h"
 #include "odil/DataSet.h"
+#include "odil/message/CStoreRequest.h"
+#include "odil/odil.h"
 #include "odil/SCU.h"
 
 namespace odil
 {
 
 /// @brief SCU for C-Store services.
-class StoreSCU: public SCU
+class ODIL_API StoreSCU: public SCU
 {
 public:
     /// @brief Constructor.
@@ -36,6 +38,14 @@ public:
         DataSet const & dataset,
         Value::String const & move_originator_ae_title = "",
         Value::Integer move_originator_message_id = -1) const;
+
+    /// @brief Perform the C-STORE.
+    void store(
+        DataSet && dataset,
+        Value::String const & move_originator_ae_title = "",
+        Value::Integer move_originator_message_id = -1) const;
+private:
+    void _store(message::CStoreRequest const & request) const;
 };
 
 }
diff --git a/src/odil/Tag.h b/src/odil/Tag.h
index a3947b3..9a59d45 100644
--- a/src/odil/Tag.h
+++ b/src/odil/Tag.h
@@ -13,13 +13,15 @@
 #include <ostream>
 #include <string>
 
+#include "odil/odil.h"
+
 namespace odil
 {
 
 /**
  * @brief A DICOM element tag.
  */
-class Tag
+class ODIL_API Tag
 {
 public:
     /// @brief Create a tag based on its group and element as two 16-bits words.
@@ -48,6 +50,16 @@ public:
      */
     Tag(char const * string);
 
+    /** @addtogroup default_operations Default class operations
+     * @{
+     */
+    ~Tag() =default;
+    Tag(Tag const &) =default;
+    Tag(Tag &&) =default;
+    Tag & operator=(Tag const &) =default;
+    Tag & operator=(Tag &&) =default;
+    /// @}
+
     /// @brief Group of the tag.
     uint16_t group;
 
@@ -91,7 +103,7 @@ private:
 };
 
 /// @brief Stream inserter
-std::ostream & operator<<(std::ostream & stream, Tag const & tag);
+ODIL_API std::ostream & operator<<(std::ostream & stream, Tag const & tag);
 
 }
 
diff --git a/src/odil/UIDsDictionary.h b/src/odil/UIDsDictionary.h
index f2523a9..b014923 100644
--- a/src/odil/UIDsDictionary.h
+++ b/src/odil/UIDsDictionary.h
@@ -12,13 +12,15 @@
 #include <map>
 #include <string>
 
+#include "odil/odil.h"
+
 namespace odil
 {
 
 /**
  * @brief Entry in a dictionary of DICOM UIDs.
  */
-struct UIDsDictionaryEntry
+struct ODIL_API UIDsDictionaryEntry
 {
     /// @brief Full name.
     std::string name;
diff --git a/src/odil/VR.h b/src/odil/VR.h
index 4dda97e..e0cd3bb 100644
--- a/src/odil/VR.h
+++ b/src/odil/VR.h
@@ -11,6 +11,8 @@
 
 #include <string>
 
+#include "odil/odil.h"
+
 namespace odil
 {
 
@@ -26,33 +28,33 @@ enum class VR
 };
 
 /// @brief Convert a VR to its string representation.
-std::string as_string(VR vr);
+ODIL_API std::string as_string(VR vr);
 
 /**
  * @brief Convert a string to its VR.
  *
  * If the string does not represent a VR, a odil::Exception is raised.
  */
-VR as_vr(std::string const & vr);
+ODIL_API VR as_vr(std::string const & vr);
 
 /**
  * @brief Guess a VR from a tag.
  *
  * If the VR cannot be guessed, a odil::Exception is raised.
  */
-VR as_vr(Tag const & tag);
+ODIL_API VR as_vr(Tag const & tag);
 
 /// @brief Test whether a VR contains integers.
-bool is_int(VR vr);
+ODIL_API bool is_int(VR vr);
 
 /// @brief Test whether a VR contains rel numbers.
-bool is_real(VR vr);
+ODIL_API bool is_real(VR vr);
 
 /// @brief Test whether a VR contains text.
-bool is_string(VR vr);
+ODIL_API bool is_string(VR vr);
 
 /// @brief Test whether a VR contains binary data.
-bool is_binary(VR vr);
+ODIL_API bool is_binary(VR vr);
 
 }
 
diff --git a/src/odil/VRFinder.h b/src/odil/VRFinder.h
index c279609..9585633 100644
--- a/src/odil/VRFinder.h
+++ b/src/odil/VRFinder.h
@@ -14,6 +14,7 @@
 #include <vector>
 
 #include "odil/DataSet.h"
+#include "odil/odil.h"
 #include "odil/Tag.h"
 #include "odil/VR.h"
 
@@ -21,7 +22,7 @@ namespace odil
 {
 
 /// @brief Find the VR of elements in an implicit VR data set.
-class VRFinder
+class ODIL_API VRFinder
 {
 public:
     /**
diff --git a/src/odil/Value.cpp b/src/odil/Value.cpp
index af2f77b..44cebc5 100644
--- a/src/odil/Value.cpp
+++ b/src/odil/Value.cpp
@@ -10,91 +10,113 @@
 
 #include <cstdint>
 #include <initializer_list>
+#include <memory>
 #include <string>
+#include <utility>
 #include <vector>
 
 #include "odil/DataSet.h"
 #include "odil/Exception.h"
 
-namespace odil
+namespace
 {
 
-Value
-::Value()
-: _type(Type::Empty)
+struct IsEmptyValue
 {
-    // Nothing else.
-}
+    typedef bool result_type;
 
-Value
-::Value(Integers const & integers)
-: _type(Type::Integers), _integers(integers)
-{
-    // Nothing else.
-}
+    template <typename T>
+    result_type operator()(T const & container) const
+    {
+        return container.empty();
+    }
+};
 
-Value
-::Value(Reals const & reals)
-: _type(Type::Reals), _reals(reals)
+struct ValueSizeGetter
 {
-    // Nothing else.
-}
+    typedef std::size_t result_type;
 
-Value
-::Value(Strings const & strings)
-: _type(Type::Strings), _strings(strings)
-{
-    // Nothing else.
-}
+    template <typename T>
+    result_type operator()(T const & container) const
+    {
+        return container.size();
+    }
+};
 
-Value
-::Value(DataSets const & datasets)
-: _type(Type::DataSets), _data_sets(datasets)
+struct ClearValue
 {
-    // Nothing else.
+    typedef void result_type;
+
+    template <typename T>
+    result_type operator()(T & container) const
+    {
+        return container.clear();
+    }
+};
 }
 
-Value
-::Value(Binary const & binary)
-: _type(Type::Binary), _binary(binary)
+namespace odil
 {
-    // Nothing else.
-}
+
+#define ODIL_VALUE_CONSTRUCTORS(type, holder) \
+    Value\
+    ::Value(type const & value)\
+    : _type(Type::type), holder(value) \
+    {} \
+    \
+    Value\
+    ::Value(type && value)\
+    : _type(Type::type), holder(std::move(value)) \
+    {} \
+    \
+    Value\
+    ::Value(std::initializer_list<type::value_type> const & value)\
+    : _type(Type::type), holder(value) \
+    {}
+    /*
+     * No need for for a rvalue reference version of std::initializer_list:
+     * copying a std::initializer_list does not copy the underlying objects.
+     */
+
+    ODIL_VALUE_CONSTRUCTORS(Integers, _integers);
+    ODIL_VALUE_CONSTRUCTORS(Reals, _reals);
+    ODIL_VALUE_CONSTRUCTORS(Strings, _strings);
+    ODIL_VALUE_CONSTRUCTORS(Binary, _binary);
+
+#undef ODIL_VALUE_CONSTRUCTORS
 
 Value
-::Value(std::initializer_list<int> const & list)
-: _type(Type::Integers)
+::Value(DataSets const & value)
+: _type(Type::DataSets), _data_sets(std::make_shared<DataSets>(value))
 {
-    this->_integers.resize(list.size());
-    std::copy(list.begin(), list.end(), this->_integers.begin());
 }
 
 Value
-::Value(std::initializer_list<Integer> const & list)
-: _type(Type::Integers), _integers(list)
+::Value(DataSets && value)
+: _type(Type::DataSets), _data_sets(std::make_shared<DataSets>(std::move(value)))
 {
-    // Nothing else
 }
 
 Value
-::Value(std::initializer_list<Real> const & list)
-: _type(Type::Reals), _reals(list)
+::Value(std::initializer_list<DataSets::value_type> const & value)
+: _type(Type::DataSets), _data_sets(std::make_shared<DataSets>(value))
 {
-    // Nothing else
 }
 
 Value
-::Value(std::initializer_list<String> const & list)
-: _type(Type::Strings), _strings(list)
+::Value(std::initializer_list<int> const & value)
+: _type(Type::Integers)
 {
-    // Nothing else
+    this->_integers.resize(value.size());
+    std::copy(value.begin(), value.end(), this->_integers.begin());
 }
 
 Value
-::Value(std::initializer_list<DataSet> const & list)
-: _type(Type::DataSets), _data_sets(list)
+::Value(std::initializer_list<std::initializer_list<uint8_t>> const & value)
+: _type(Type::Binary)
 {
-    // Nothing else
+    this->_binary.resize(value.size());
+    std::copy(value.begin(), value.end(), this->_binary.begin());
 }
 
 Value::Type
@@ -108,7 +130,14 @@ bool
 Value
 ::empty() const
 {
-    return (this->_type == Type::Empty);
+    return apply_visitor(IsEmptyValue(), *this);
+}
+
+std::size_t
+Value
+::size() const
+{
+    return apply_visitor(ValueSizeGetter(), *this);
 }
 
 #define DECLARE_CONST_ACCESSOR(type, name) \
@@ -144,8 +173,27 @@ DECLARE_NON_CONST_ACCESSOR(Reals, reals)
 DECLARE_CONST_ACCESSOR(Strings, strings)
 DECLARE_NON_CONST_ACCESSOR(Strings, strings)
 
-DECLARE_CONST_ACCESSOR(DataSets, data_sets)
-DECLARE_NON_CONST_ACCESSOR(DataSets, data_sets)
+Value::DataSets const & 
+Value
+::as_data_sets() const
+{
+    if(this->get_type() != Type::DataSets)
+    {
+        throw Exception("Type mismatch");
+    }
+	return *this->_data_sets;
+}
+
+Value::DataSets &
+Value
+::as_data_sets()
+{
+    if(this->get_type() != Type::DataSets)
+    {
+        throw Exception("Type mismatch");
+    }
+	return *this->_data_sets;
+}
 
 DECLARE_CONST_ACCESSOR(Binary, binary)
 DECLARE_NON_CONST_ACCESSOR(Binary, binary)
@@ -161,10 +209,6 @@ Value
     {
         return false;
     }
-    else if(this->_type == Value::Type::Empty)
-    {
-        return true;
-    }
     else if(this->_type == Value::Type::Integers)
     {
         return this->_integers == other._integers;
@@ -179,7 +223,7 @@ Value
     }
     else if(this->_type == Value::Type::DataSets)
     {
-        return this->_data_sets == other._data_sets;
+        return *(this->_data_sets) == *(other._data_sets);
     }
     else if(this->_type == Value::Type::Binary)
     {
@@ -198,6 +242,12 @@ Value
     return !(*this == other);
 }
 
-
+void
+Value
+::clear()
+{
+    apply_visitor(ClearValue(), *this);
+}
 
 }
+
diff --git a/src/odil/Value.h b/src/odil/Value.h
index 29b9828..323ce07 100644
--- a/src/odil/Value.h
+++ b/src/odil/Value.h
@@ -11,9 +11,12 @@
 
 #include <cstdint>
 #include <initializer_list>
+#include <memory>
 #include <string>
 #include <vector>
 
+#include "odil/odil.h"
+
 namespace odil
 {
 
@@ -22,13 +25,12 @@ class DataSet;
 /**
  * @brief A value held in a DICOM element.
  */
-class Value
+class ODIL_API Value
 {
 public:
     /// @brief Possible types stored in the value.
     enum class Type
     {
-        Empty,
         Integers,
         Reals,
         Strings,
@@ -60,38 +62,35 @@ public:
     /// @brief Binary data container.
     typedef std::vector<std::vector<uint8_t>> Binary;
 
-    /// @brief Build an empty value.
-    Value();
-
-    /// @brief Build a value from integers.
-    Value(Integers const & integers);
-
-    /// @brief Build a value from reals.
-    Value(Reals const & reals);
-
-    /// @brief Build a value from strings.
-    Value(Strings const & strings);
-
-    /// @brief Build a value from data sets.
-    Value(DataSets const & datasets);
-
-    /// @brief Build a value from binary data.
-    Value(Binary const & binary);
-
-    /// @brief Build a value from integers.
-    Value(std::initializer_list<int> const & list);
+#define ODIL_VALUE_CONSTRUCTORS(type) \
+    Value(type const & value); \
+    Value(type && value); \
+    Value(std::initializer_list<type::value_type> const & value);
+    /*
+     * No need for for a rvalue reference version of std::initializer_list:
+     * copying a std::initializer_list does not copy the underlying objects.
+     */
 
-    /// @brief Build a value from integers.
-    Value(std::initializer_list<Integer> const & list);
+    ODIL_VALUE_CONSTRUCTORS(Integers);
+    ODIL_VALUE_CONSTRUCTORS(Reals);
+    ODIL_VALUE_CONSTRUCTORS(Strings);
+    ODIL_VALUE_CONSTRUCTORS(DataSets);
+    ODIL_VALUE_CONSTRUCTORS(Binary);
+#undef ODIL_VALUE_CONSTRUCTORS
 
-    /// @brief Build a value from reals.
-    Value(std::initializer_list<Real> const & list);
+    Value(std::initializer_list<int> const & value);
 
-    /// @brief Build a value from strings.
-    Value(std::initializer_list<String> const & list);
+    Value(std::initializer_list<std::initializer_list<uint8_t>> const & value);
 
-    /// @brief Build a value from data sets.
-    Value(std::initializer_list<DataSet> const & list);
+    /** @addtogroup default_operations Default class operations
+     * @{
+     */
+    ~Value() =default;
+    Value(Value const &) =default;
+    Value(Value &&) =default;
+    Value & operator=(Value const &) =default;
+    Value & operator=(Value &&) =default;
+    /// @}
 
     /// @brief Return the type store in the value.
     Type get_type() const;
@@ -99,6 +98,9 @@ public:
     /// @brief Test whether the value is empty.
     bool empty() const;
 
+    /// @brief Return the number of items.
+    std::size_t size() const;
+
     /**
      * @brief Return the integers contained in the value.
      *
@@ -175,11 +177,16 @@ public:
     /// @brief Difference test.
     bool operator!=(Value const & other) const;
 
+    /// @brief Clear the value (value.empty() will be true).
+    void clear();
+
 private:
     Integers _integers;
     Reals _reals;
     Strings _strings;
-    DataSets _data_sets;
+    // NOTE: can't use std::vector<DataSet> with forward-declaration of DataSet
+    // cf. C++11, 17.6.4.8, last bullet of clause 2
+    std::shared_ptr<DataSets> _data_sets;
     Binary _binary;
 
     Type _type;
diff --git a/src/odil/Value.txx b/src/odil/Value.txx
index 765676f..6a8826d 100644
--- a/src/odil/Value.txx
+++ b/src/odil/Value.txx
@@ -19,11 +19,7 @@ template<typename TVisitor>
 typename TVisitor::result_type
 apply_visitor(TVisitor const & visitor, Value const & value)
 {
-    if(value.get_type() == Value::Type::Empty)
-    {
-        throw Exception("Empty value");
-    }
-    else if(value.get_type() == Value::Type::Integers)
+    if(value.get_type() == Value::Type::Integers)
     {
         return visitor(value.as_integers());
     }
@@ -53,11 +49,7 @@ template<typename TVisitor>
 typename TVisitor::result_type
 apply_visitor(TVisitor const & visitor, Value & value)
 {
-    if(value.get_type() == Value::Type::Empty)
-    {
-        throw Exception("Empty value");
-    }
-    else if(value.get_type() == Value::Type::Integers)
+    if(value.get_type() == Value::Type::Integers)
     {
         return visitor(value.as_integers());
     }
diff --git a/src/odil/Writer.h b/src/odil/Writer.h
index b7eb63f..166f1f6 100644
--- a/src/odil/Writer.h
+++ b/src/odil/Writer.h
@@ -15,6 +15,7 @@
 #include "odil/DataSet.h"
 #include "odil/Element.h"
 #include "odil/endian.h"
+#include "odil/odil.h"
 #include "odil/registry.h"
 #include "odil/Tag.h"
 #include "odil/Value.h"
@@ -24,7 +25,7 @@ namespace odil
 {
 
 /// @brief Write DICOM objects to a stream.
-class Writer
+class ODIL_API Writer
 {
 public:
     /// @brief Encodings of sequence items.
diff --git a/src/odil/base64.h b/src/odil/base64.h
index 3619886..c2b5e6e 100644
--- a/src/odil/base64.h
+++ b/src/odil/base64.h
@@ -11,6 +11,8 @@
 
 #include <string>
 
+#include "odil/odil.h"
+
 namespace odil
 {
 
@@ -18,10 +20,10 @@ namespace base64
 {
 
 /// @brief Dictionary of symbols for Base64.
-extern std::string const symbols;
+extern ODIL_API std::string const symbols;
 
 /// @brief Mapping of ASCII characters to values of Base64 symbols.
-extern std::string const reversed_symbols;
+extern ODIL_API std::string const reversed_symbols;
 
 /// @brief Encode a sequence of 8 bits data to Base64.
 template<typename TInputIterator, typename TOutputIterator>
diff --git a/src/odil/dcmtk/ElementAccessor.h b/src/odil/dcmtk/ElementAccessor.h
index cbaaf7f..24a4224 100644
--- a/src/odil/dcmtk/ElementAccessor.h
+++ b/src/odil/dcmtk/ElementAccessor.h
@@ -17,6 +17,7 @@
 
 #include "odil/dcmtk/ElementTraits.h"
 #include "odil/dcmtk/Exception.h"
+#include "odil/odil.h"
 
 namespace odil
 {
@@ -36,14 +37,14 @@ struct ElementAccessor
         ValueType(DcmElement const &, unsigned long const)> GetterType;
 
     /// @brief Return the value in the element.
-    static GetterType const element_get;
+    static ODIL_API GetterType const element_get;
 
     /// @brief Setter type.
     typedef std::function<
         void(DcmElement &, ValueType const &, unsigned long const)> SetterType;
 
     /// @brief Set the value in the element.
-    static SetterType const element_set;
+    static ODIL_API SetterType const element_set;
 
     /// @brief Test whether the data set contains a given tag.
     static bool has(DcmDataset const & dataset, DcmTagKey const & tag);
diff --git a/src/odil/dcmtk/Exception.h b/src/odil/dcmtk/Exception.h
index 38a6b30..3a1668b 100644
--- a/src/odil/dcmtk/Exception.h
+++ b/src/odil/dcmtk/Exception.h
@@ -13,6 +13,7 @@
 #include <dcmtk/ofstd/ofcond.h>
 
 #include "odil/Exception.h"
+#include "odil/odil.h"
 
 namespace odil
 {
@@ -20,7 +21,7 @@ namespace odil
 namespace dcmtk
 {
 
-class Exception: public ::odil::Exception
+class ODIL_API Exception: public ::odil::Exception
 {
 public:
     /**
diff --git a/src/odil/dcmtk/conversion.cpp b/src/odil/dcmtk/conversion.cpp
index d4ce23c..55a856a 100644
--- a/src/odil/dcmtk/conversion.cpp
+++ b/src/odil/dcmtk/conversion.cpp
@@ -8,6 +8,8 @@
 
 #include "odil/dcmtk/conversion.h"
 
+#include <memory>
+
 #include <dcmtk/config/osconfig.h>
 #include <dcmtk/dcmdata/dctk.h>
 
@@ -341,7 +343,7 @@ void convert<std::vector<uint8_t>, Value::Binary>(
 
 Element convert(DcmElement * source)
 {
-    Element destination;
+    std::shared_ptr<Element> destination;
 
     DcmEVR const source_vr = source->getTag().getVR().getValidEVR();
     VR const destination_vr = convert(source_vr);
@@ -352,13 +354,13 @@ Element convert(DcmElement * source)
        source_vr == EVR_ST || source_vr == EVR_TM || source_vr == EVR_UI ||
        source_vr == EVR_UT)
     {
-        destination = Element(Value::Strings(), destination_vr);
-        convert<std::string, Value::Strings>(source, destination, &Element::as_string);
+        destination = std::make_shared<Element>(Value::Strings(), destination_vr);
+        convert<std::string, Value::Strings>(source, *destination, &Element::as_string);
     }
     else if(source_vr == EVR_AT)
     {
-        destination = Element(Value::Strings(), destination_vr);
-        destination.as_string().reserve(source->getVM());
+        destination = std::make_shared<Element>(Value::Strings(), destination_vr);
+        destination->as_string().reserve(source->getVM());
         for(unsigned int i=0; i<source->getVM(); ++i)
         {
             DcmTagKey source_tag;
@@ -368,34 +370,34 @@ Element convert(DcmElement * source)
                 throw Exception(condition);
             }
             Tag const destination_tag = convert(source_tag);
-            destination.as_string().push_back(std::string(destination_tag));
+            destination->as_string().push_back(std::string(destination_tag));
         }
     }
     else if(source_vr == EVR_DS || source_vr == EVR_FD)
     {
-        destination = Element(Value::Reals(), destination_vr);
-        convert<Float64, Value::Reals>(source, destination, &Element::as_real);
+        destination = std::make_shared<Element>(Value::Reals(), destination_vr);
+        convert<Float64, Value::Reals>(source, *destination, &Element::as_real);
     }
     else if(source_vr == EVR_FL)
     {
-        destination = Element(Value::Reals(), destination_vr);
-        convert<Float32, Value::Reals>(source, destination, &Element::as_real);
+        destination = std::make_shared<Element>(Value::Reals(), destination_vr);
+        convert<Float32, Value::Reals>(source, *destination, &Element::as_real);
     }
     else if(source_vr == EVR_IS || source_vr == EVR_SL)
     {
-        destination = Element(Value::Integers(), destination_vr);
-        convert<Sint32, Value::Integers>(source, destination, &Element::as_int);
+        destination = std::make_shared<Element>(Value::Integers(), destination_vr);
+        convert<Sint32, Value::Integers>(source, *destination, &Element::as_int);
     }
     else if(source_vr == EVR_SQ)
     {
-        destination = Element(Value::DataSets(), destination_vr);
+        destination = std::make_shared<Element>(Value::DataSets(), destination_vr);
         DcmSequenceOfItems * sequence = dynamic_cast<DcmSequenceOfItems*>(source);
         if(sequence == NULL)
         {
             throw Exception("Element is not a DcmSequenceOfItems");
         }
 
-        Value::DataSets & destination_value = destination.as_data_set();
+        Value::DataSets & destination_value = destination->as_data_set();
 
         destination_value.reserve(sequence->card());
         for(unsigned int i=0; i<sequence->card(); ++i)
@@ -407,31 +409,31 @@ Element convert(DcmElement * source)
     }
     else if(source_vr == EVR_SS)
     {
-        destination = Element(Value::Integers(), destination_vr);
-        convert<Sint16, Value::Integers>(source, destination, &Element::as_int);
+        destination = std::make_shared<Element>(Value::Integers(), destination_vr);
+        convert<Sint16, Value::Integers>(source, *destination, &Element::as_int);
     }
     else if(source_vr == EVR_UL)
     {
-        destination = Element(Value::Integers(), destination_vr);
-        convert<Uint32, Value::Integers>(source, destination, &Element::as_int);
+        destination = std::make_shared<Element>(Value::Integers(), destination_vr);
+        convert<Uint32, Value::Integers>(source, *destination, &Element::as_int);
     }
     else if(source_vr == EVR_OB || source_vr == EVR_OF || source_vr == EVR_OW ||
             source_vr == EVR_UN)
     {
-        destination = Element(Value::Binary(), destination_vr);
-        convert<std::vector<uint8_t>, Value::Binary>(source, destination, &Element::as_binary);
+        destination = std::make_shared<Element>(Value::Binary(), destination_vr);
+        convert<std::vector<uint8_t>, Value::Binary>(source, *destination, &Element::as_binary);
     }
     else if(source_vr == EVR_US)
     {
-        destination = Element(Value::Integers(), destination_vr);
-        convert<Uint16, Value::Integers>(source, destination, &Element::as_int);
+        destination = std::make_shared<Element>(Value::Integers(), destination_vr);
+        convert<Uint16, Value::Integers>(source, *destination, &Element::as_int);
     }
     else
     {
         throw Exception("Unknown VR: "+std::string(DcmVR(source_vr).getVRName()));
     }
 
-    return destination;
+    return *destination;
 }
 
 void convert(Element const & source, DcmOtherByteOtherWord * destination)
diff --git a/src/odil/dcmtk/conversion.h b/src/odil/dcmtk/conversion.h
index fddc6fc..3717180 100644
--- a/src/odil/dcmtk/conversion.h
+++ b/src/odil/dcmtk/conversion.h
@@ -14,6 +14,7 @@
 
 #include "odil/DataSet.h"
 #include "odil/Element.h"
+#include "odil/odil.h"
 #include "odil/Tag.h"
 #include "odil/VR.h"
 
@@ -24,19 +25,19 @@ namespace dcmtk
 {
 
 /// @brief Convert a odil::VR to a DcmVR.
-DcmEVR convert(VR vr);
+ODIL_API DcmEVR convert(VR vr);
 
 /// @brief Convert a DcmVR to a odil::VR.
-VR convert(DcmEVR evr);
+ODIL_API VR convert(DcmEVR evr);
 
 /// @brief Convert a odil::Tag to a DcmTagKey.
-DcmTagKey convert(Tag const & tag);
+ODIL_API DcmTagKey convert(Tag const & tag);
 
 /// @brief Convert a DcmTagKey to a odil::Tag.
-Tag convert(DcmTagKey const & tag);
+ODIL_API Tag convert(DcmTagKey const & tag);
 
 /// @brief Convert a odil::Element to a DcmElement.
-DcmElement * convert(Tag const & tag, Element const & source);
+ODIL_API DcmElement * convert(Tag const & tag, Element const & source);
 
 /// @brief Low-level element converter.
 template<typename TSourceType, typename TDestinationType>
@@ -45,7 +46,7 @@ void convert(
     TSourceType const & (Element::*getter)() const);
 
 /// @brief Convert a DcmElement to a odil::Element.
-Element convert(DcmElement * source);
+ODIL_API Element convert(DcmElement * source);
 
 /// @brief Low-level element converter.
 template<typename TSourceType>
@@ -54,10 +55,11 @@ void convert(
     TSourceType const & (Element::*getter)() const);
 
 /// @brief Low-level element converter.
+ODIL_API
 void convert(Element const & source, DcmOtherByteOtherWord * destination);
 
 /// @brief Low-level element converter.
-void convert(Element const & source, DcmOtherFloat * destination);
+ODIL_API void convert(Element const & source, DcmOtherFloat * destination);
 
 /// @brief Low-level element converter.
 template<typename TSourceType, typename TDestinationType>
@@ -66,10 +68,10 @@ void convert(
     TDestinationType & (Element::*getter)());
 
 /// @brief Convert a odil::DataSet to a DcmDataset or a DcmItem.
-DcmItem * convert(DataSet const & source, bool as_data_set=true);
+ODIL_API DcmItem * convert(DataSet const & source, bool as_data_set=true);
 
 /// @brief Convert a DcmDataset to a odil::DataSet.
-DataSet convert(DcmItem * source);
+ODIL_API DataSet convert(DcmItem * source);
 
 }
 
diff --git a/src/odil/dul/EventData.h b/src/odil/dul/EventData.h
index 9402309..bae2ab2 100644
--- a/src/odil/dul/EventData.h
+++ b/src/odil/dul/EventData.h
@@ -14,6 +14,7 @@
 #include "odil/AssociationAcceptor.h"
 #include "odil/AssociationParameters.h"
 #include "odil/dul/Transport.h"
+#include "odil/odil.h"
 #include "odil/pdu/Object.h"
 
 namespace odil
@@ -23,7 +24,7 @@ namespace dul
 {
 
 /// @brief Data related to events of the DUL state machine.
-class EventData
+class ODIL_API EventData
 {
 public:
     Transport::Socket::endpoint_type peer_endpoint;
diff --git a/src/odil/dul/StateMachine.h b/src/odil/dul/StateMachine.h
index a0c9703..f45688a 100644
--- a/src/odil/dul/StateMachine.h
+++ b/src/odil/dul/StateMachine.h
@@ -19,6 +19,7 @@
 #include "odil/AssociationAcceptor.h"
 #include "odil/dul/EventData.h"
 #include "odil/dul/Transport.h"
+#include "odil/odil.h"
 
 namespace odil
 {
@@ -27,7 +28,7 @@ namespace dul
 {
 
 /// @brief State machine for the DICOM upper layer.
-class StateMachine
+class ODIL_API StateMachine
 {
 public:
 
diff --git a/src/odil/dul/Transport.h b/src/odil/dul/Transport.h
index 8b9d046..93a6bab 100644
--- a/src/odil/dul/Transport.h
+++ b/src/odil/dul/Transport.h
@@ -15,6 +15,8 @@
 #include <boost/asio.hpp>
 #include <boost/date_time.hpp>
 
+#include "odil/odil.h"
+
 namespace odil
 {
 
@@ -28,7 +30,7 @@ namespace dul
  * value: if the timeout expires before the operation is completed, an exception
  * will be raised.
  */
-struct Transport
+struct ODIL_API Transport
 {
     /// @brief Socket type.
     typedef boost::asio::ip::tcp::socket Socket;
diff --git a/src/odil/json_converter.cpp b/src/odil/json_converter.cpp
index c9a970f..2a3f0c9 100644
--- a/src/odil/json_converter.cpp
+++ b/src/odil/json_converter.cpp
@@ -239,12 +239,10 @@ DataSet as_dataset(Json::Value const & json)
         Json::Value const & json_element = *it;
         VR const vr = as_vr(json_element["vr"].asString());
 
-        Element element;
+        Element element(vr);
 
         if(odil::is_string(vr) && vr != odil::VR::PN)
         {
-            element = Element(Value::Strings(), vr);
-
             auto const & json_value = json_element["Value"];
             for(auto const & json_item: json_value)
             {
@@ -253,7 +251,6 @@ DataSet as_dataset(Json::Value const & json)
         }
         else if(vr == VR::PN)
         {
-            element = Element(Value::Strings(), vr);
             auto const & json_value = json_element["Value"];
             for(auto const & json_item: json_value)
             {   
@@ -278,8 +275,6 @@ DataSet as_dataset(Json::Value const & json)
         }
         else if(is_real(vr))
         {
-            element = Element(Value::Reals(), vr);
-
             auto const & json_value = json_element["Value"];
             for(auto const & json_item: json_value)
             {
@@ -288,8 +283,6 @@ DataSet as_dataset(Json::Value const & json)
         }
         else if(is_int(vr))
         {
-            element = Element(Value::Integers(), vr);
-
             auto const & json_value = json_element["Value"];
             for(auto const & json_item: json_value)
             {
@@ -298,7 +291,6 @@ DataSet as_dataset(Json::Value const & json)
         }
         else if(vr == VR::SQ)
         {
-            element = Element(Value::DataSets(), vr);
             auto const & json_value = json_element["Value"];
             for(auto const & json_item: json_value)
             {
@@ -308,8 +300,6 @@ DataSet as_dataset(Json::Value const & json)
         }
         else if(is_binary(vr))
         {
-            element = Element(Value::Binary(), vr);
-
             // cf. ToJSONVisitor::operator()(VR, Value::Binary): InlineBinary is
             // single-valued
             auto const & encoded = json_element["InlineBinary"].asString();
diff --git a/src/odil/json_converter.h b/src/odil/json_converter.h
index f4bcab7..529df68 100644
--- a/src/odil/json_converter.h
+++ b/src/odil/json_converter.h
@@ -12,18 +12,19 @@
 #include <json/json.h>
 
 #include "odil/DataSet.h"
+#include "odil/odil.h"
 #include "odil/Value.h"
 
 namespace odil
 {
 
 /// @brief Convert a data set to its JSON representation.
-Json::Value as_json(
+ODIL_API Json::Value as_json(
     DataSet const & data_set,
     odil::Value::Strings const & specific_character_set=odil::Value::Strings());
 
 /// @brief Create a data set from its JSON representation.
-DataSet as_dataset(Json::Value const & json);
+ODIL_API DataSet as_dataset(Json::Value const & json);
 
 }
 
diff --git a/src/odil/message/CEchoRequest.h b/src/odil/message/CEchoRequest.h
index d8a2f3c..494b2a5 100644
--- a/src/odil/message/CEchoRequest.h
+++ b/src/odil/message/CEchoRequest.h
@@ -10,6 +10,7 @@
 #define _aec786b8_0074_4cb2_b9a1_4bf26bbd20fc
 
 #include "odil/message/Request.h"
+#include "odil/odil.h"
 #include "odil/registry.h"
 #include "odil/Value.h"
 
@@ -20,7 +21,7 @@ namespace message
 {
 
 /// @brief C-ECHO-RQ message.
-class CEchoRequest: public Request
+class ODIL_API CEchoRequest: public Request
 {
 public:
     /**
diff --git a/src/odil/message/CEchoResponse.h b/src/odil/message/CEchoResponse.h
index 45b13d6..abb4108 100644
--- a/src/odil/message/CEchoResponse.h
+++ b/src/odil/message/CEchoResponse.h
@@ -9,6 +9,7 @@
 #ifndef _266252d9_e801_479e_a805_004b101c5250
 #define _266252d9_e801_479e_a805_004b101c5250
 
+#include "odil/odil.h"
 #include "odil/registry.h"
 #include "odil/message/Response.h"
 #include "odil/Value.h"
@@ -20,7 +21,7 @@ namespace message
 {
 
 /// @brief C-ECHO-RSP message.
-class CEchoResponse: public Response
+class ODIL_API CEchoResponse: public Response
 {
 public:
     /**
diff --git a/src/odil/message/CFindRequest.cpp b/src/odil/message/CFindRequest.cpp
index 032a74d..10c1df0 100644
--- a/src/odil/message/CFindRequest.cpp
+++ b/src/odil/message/CFindRequest.cpp
@@ -27,6 +27,48 @@ CFindRequest
     Value::Integer priority, DataSet const & dataset)
 : Request(message_id)
 {
+    this->_create(affected_sop_class_uid, priority, dataset);
+    this->set_data_set(dataset);
+}
+
+CFindRequest
+::CFindRequest(
+    Value::Integer message_id, Value::String const & affected_sop_class_uid,
+    Value::Integer priority, DataSet && dataset)
+: Request(message_id)
+{
+    this->_create(affected_sop_class_uid, priority, dataset);
+    this->set_data_set(std::move(dataset));
+}
+
+CFindRequest
+::CFindRequest(Message const & message)
+: Request(message)
+{
+    this->_parse(message);
+    this->set_data_set(message.get_data_set());
+}
+
+CFindRequest
+::CFindRequest(Message && message)
+: Request(message)
+{
+    this->_parse(message);
+    this->set_data_set(std::move(message.get_data_set()));
+}
+
+CFindRequest
+::~CFindRequest()
+{
+    // Nothing to do.
+}
+
+void
+CFindRequest
+::_create(
+    Value::String const & affected_sop_class_uid, Value::Integer priority,
+    DataSet const & dataset)
+{
     this->set_command_field(Command::C_FIND_RQ);
     this->set_affected_sop_class_uid(affected_sop_class_uid);
     this->set_priority(priority);
@@ -34,12 +76,11 @@ CFindRequest
     {
         throw Exception("Data set is required");
     }
-    this->set_data_set(dataset);
 }
 
+void
 CFindRequest
-::CFindRequest(Message const & message)
-: Request(message)
+::_parse(Message const & message)
 {
     if(message.get_command_field() != Command::C_FIND_RQ)
     {
@@ -56,13 +97,6 @@ CFindRequest
     {
         throw Exception("Data set is required");
     }
-    this->set_data_set(message.get_data_set());
-}
-
-CFindRequest
-::~CFindRequest()
-{
-    // Nothing to do.
 }
 
 }
diff --git a/src/odil/message/CFindRequest.h b/src/odil/message/CFindRequest.h
index f3a23cc..e3f571a 100644
--- a/src/odil/message/CFindRequest.h
+++ b/src/odil/message/CFindRequest.h
@@ -9,6 +9,7 @@
 #ifndef _74cfa9e7_da35_4130_a941_e17cb6932f60
 #define _74cfa9e7_da35_4130_a941_e17cb6932f60
 
+#include "odil/odil.h"
 #include "odil/registry.h"
 #include "odil/message/Request.h"
 #include "odil/Value.h"
@@ -20,7 +21,7 @@ namespace message
 {
 
 /// @brief C-FIND-RQ message.
-class CFindRequest: public Request
+class ODIL_API CFindRequest: public Request
 {
 public:
     /**
@@ -32,18 +33,39 @@ public:
         Value::Integer priority, DataSet const & dataset);
 
     /**
+     * @brief Create an find request with given Message ID,
+     * affected SOP class UID, priority, and data set.
+     */
+    CFindRequest(
+        Value::Integer message_id, Value::String const & affected_sop_class_uid,
+        Value::Integer priority, DataSet && dataset);
+
+    /**
      * @brief Create a C-FIND-RQ from a generic Message.
      *
      * Raise an exception if the Message does not contain a C-FIND-RQ.
      */
     CFindRequest(Message const & message);
 
+    /**
+     * @brief Create a C-FIND-RQ from a generic Message.
+     *
+     * Raise an exception if the Message does not contain a C-FIND-RQ.
+     */
+    CFindRequest(Message && message);
+
     /// @brief Destructor.
     virtual ~CFindRequest();
     
     ODIL_MESSAGE_MANDATORY_FIELD_STRING_MACRO(
         affected_sop_class_uid, registry::AffectedSOPClassUID)
     ODIL_MESSAGE_MANDATORY_FIELD_INTEGER_MACRO(priority, registry::Priority)
+
+private:
+    void _create(
+        Value::String const & affected_sop_class_uid, Value::Integer priority,
+        DataSet const & dataset);
+    void _parse(Message const & message);
 };
 
 }
diff --git a/src/odil/message/CFindResponse.cpp b/src/odil/message/CFindResponse.cpp
index c118dbe..39547fd 100644
--- a/src/odil/message/CFindResponse.cpp
+++ b/src/odil/message/CFindResponse.cpp
@@ -39,24 +39,34 @@ CFindResponse
 }
 
 CFindResponse
+::CFindResponse(
+    Value::Integer message_id_being_responded_to, Value::Integer status,
+    DataSet && dataset)
+: Response(message_id_being_responded_to, status)
+{
+    this->set_command_field(Command::C_FIND_RSP);
+    this->set_data_set(std::move(dataset));
+}
+
+CFindResponse
 ::CFindResponse(Message const & message)
 : Response(message)
 {
-    if(message.get_command_field() != Command::C_FIND_RSP)
+    this->_parse(message);
+    if(message.has_data_set())
     {
-        throw Exception("Message is not a C-FIND-RSP");
+        this->set_data_set(message.get_data_set());
     }
-    this->set_command_field(message.get_command_field());
-    
-    ODIL_MESSAGE_SET_OPTIONAL_FIELD_MACRO(
-        message.get_command_set(), message_id, registry::MessageID, as_int)
-    ODIL_MESSAGE_SET_OPTIONAL_FIELD_MACRO(
-        message.get_command_set(), affected_sop_class_uid,
-        registry::AffectedSOPClassUID, as_string)
+}
 
+CFindResponse
+::CFindResponse(Message && message)
+: Response(message)
+{
+    this->_parse(message);
     if(message.has_data_set())
     {
-        this->set_data_set(message.get_data_set());
+        this->set_data_set(std::move(message.get_data_set()));
     }
 }
 
@@ -66,6 +76,23 @@ CFindResponse
     // Nothing to do.
 }
 
+void
+CFindResponse
+::_parse(Message const & message)
+{
+    if(message.get_command_field() != Command::C_FIND_RSP)
+    {
+        throw Exception("Message is not a C-FIND-RSP");
+    }
+    this->set_command_field(message.get_command_field());
+
+    ODIL_MESSAGE_SET_OPTIONAL_FIELD_MACRO(
+        message.get_command_set(), message_id, registry::MessageID, as_int)
+    ODIL_MESSAGE_SET_OPTIONAL_FIELD_MACRO(
+        message.get_command_set(), affected_sop_class_uid,
+        registry::AffectedSOPClassUID, as_string)
+}
+
 }
 
 }
diff --git a/src/odil/message/CFindResponse.h b/src/odil/message/CFindResponse.h
index a628fb0..611cae7 100644
--- a/src/odil/message/CFindResponse.h
+++ b/src/odil/message/CFindResponse.h
@@ -10,6 +10,7 @@
 #define _5fd36547_9498_4cf3_87cc_737af51e93a9
 
 #include "odil/DataSet.h"
+#include "odil/odil.h"
 #include "odil/registry.h"
 #include "odil/message/Response.h"
 #include "odil/Value.h"
@@ -21,7 +22,7 @@ namespace message
 {
 
 /// @brief C-FIND-RSP message.
-class CFindResponse: public Response
+class ODIL_API CFindResponse: public Response
 {
 public:
     /// @brief C-FIND status codes, PS 3.4, C.4.1.1.4
@@ -50,18 +51,36 @@ public:
         DataSet const & dataset);
 
     /**
+     * @brief Create an find response with given Message ID, status,
+     * and data set.
+     */
+    CFindResponse(
+        Value::Integer message_id_being_responded_to, Value::Integer status,
+        DataSet && dataset);
+
+    /**
      * @brief Create a C-FIND-RSP from a generic Message.
      *
      * Raise an exception if the Message does not contain a C-FIND-RSP.
      */
     CFindResponse(Message const & message);
 
+    /**
+     * @brief Create a C-FIND-RSP from a generic Message.
+     *
+     * Raise an exception if the Message does not contain a C-FIND-RSP.
+     */
+    CFindResponse(Message && message);
+
     /// @brief Destructor.
     virtual ~CFindResponse();
 
     ODIL_MESSAGE_OPTIONAL_FIELD_INTEGER_MACRO(message_id, registry::MessageID)
     ODIL_MESSAGE_OPTIONAL_FIELD_STRING_MACRO(
         affected_sop_class_uid, registry::AffectedSOPClassUID)
+
+private:
+    void _parse(Message const & message);
 };
 
 }
diff --git a/src/odil/message/CGetRequest.cpp b/src/odil/message/CGetRequest.cpp
index 6283fa3..af339f3 100644
--- a/src/odil/message/CGetRequest.cpp
+++ b/src/odil/message/CGetRequest.cpp
@@ -28,6 +28,48 @@ CGetRequest
     Value::Integer priority, DataSet const & dataset)
 : Request(message_id)
 {
+    this->_create(affected_sop_class_uid, priority, dataset);
+    this->set_data_set(dataset);
+}
+
+CGetRequest
+::CGetRequest(
+    Value::Integer message_id, Value::String const & affected_sop_class_uid,
+    Value::Integer priority, DataSet && dataset)
+: Request(message_id)
+{
+    this->_create(affected_sop_class_uid, priority, dataset);
+    this->set_data_set(std::move(dataset));
+}
+
+CGetRequest
+::CGetRequest(Message const & message)
+: Request(message)
+{
+    this->_parse(message);
+    this->set_data_set(message.get_data_set());
+}
+
+CGetRequest
+::CGetRequest(Message && message)
+: Request(message)
+{
+    this->_parse(message);
+    this->set_data_set(std::move(message.get_data_set()));
+}
+
+CGetRequest
+::~CGetRequest()
+{
+    // Nothing to do.
+}
+
+void
+CGetRequest
+::_create(
+    Value::String const & affected_sop_class_uid, Value::Integer priority,
+    DataSet const & dataset)
+{
     this->set_command_field(Command::C_GET_RQ);
     this->set_affected_sop_class_uid(affected_sop_class_uid);
     this->set_priority(priority);
@@ -35,12 +77,11 @@ CGetRequest
     {
         throw Exception("Data set is required");
     }
-    this->set_data_set(dataset);
 }
 
+void
 CGetRequest
-::CGetRequest(Message const & message)
-: Request(message)
+::_parse(Message const & message)
 {
     if(message.get_command_field() != Command::C_GET_RQ)
     {
@@ -57,13 +98,6 @@ CGetRequest
     {
         throw Exception("Data set is required");
     }
-    this->set_data_set(message.get_data_set());
-}
-
-CGetRequest
-::~CGetRequest()
-{
-    // Nothing to do.
 }
 
 }
diff --git a/src/odil/message/CGetRequest.h b/src/odil/message/CGetRequest.h
index 8152f5e..c2943c9 100644
--- a/src/odil/message/CGetRequest.h
+++ b/src/odil/message/CGetRequest.h
@@ -10,6 +10,7 @@
 #define _6a22f126_7cc6_47ab_81c2_5f66b2714345
 
 #include "odil/DataSet.h"
+#include "odil/odil.h"
 #include "odil/registry.h"
 #include "odil/message/Request.h"
 #include "odil/Value.h"
@@ -21,7 +22,7 @@ namespace message
 {
 
 /// @brief C-GET-RQ message.
-class CGetRequest: public Request
+class ODIL_API CGetRequest: public Request
 {
 public:
     /**
@@ -33,18 +34,39 @@ public:
         Value::Integer priority, DataSet const & dataset);
 
     /**
+     * @brief Create an get request with given Message ID,
+     * affected SOP class UID, priority, and data set.
+     */
+    CGetRequest(
+        Value::Integer message_id, Value::String const & affected_sop_class_uid,
+        Value::Integer priority, DataSet && dataset);
+
+    /**
      * @brief Create a C-GET-RQ from a generic Message.
      *
      * Raise an exception if the Message does not contain a C-GET-RQ.
      */
     CGetRequest(Message const & message);
 
+    /**
+     * @brief Create a C-GET-RQ from a generic Message.
+     *
+     * Raise an exception if the Message does not contain a C-GET-RQ.
+     */
+    CGetRequest(Message && message);
+
     /// @brief Destructor.
     virtual ~CGetRequest();
 
     ODIL_MESSAGE_MANDATORY_FIELD_STRING_MACRO(
         affected_sop_class_uid, registry::AffectedSOPClassUID)
     ODIL_MESSAGE_MANDATORY_FIELD_INTEGER_MACRO(priority, registry::Priority)
+
+private:
+    void _create(
+        Value::String const & affected_sop_class_uid, Value::Integer priority,
+        DataSet const & dataset);
+    void _parse(Message const & message);
 };
 
 }
diff --git a/src/odil/message/CGetResponse.cpp b/src/odil/message/CGetResponse.cpp
index c624da0..9c3a9b8 100644
--- a/src/odil/message/CGetResponse.cpp
+++ b/src/odil/message/CGetResponse.cpp
@@ -39,15 +39,53 @@ CGetResponse
 }
 
 CGetResponse
+::CGetResponse(
+    Value::Integer message_id_being_responded_to, Value::Integer status,
+    DataSet && dataset)
+: Response(message_id_being_responded_to, status)
+{
+    this->set_command_field(Command::C_GET_RSP);
+    this->set_data_set(std::move(dataset));
+}
+
+CGetResponse
 ::CGetResponse(Message const & message)
 : Response(message)
 {
+    this->_parse(message);
+    if(message.has_data_set())
+    {
+        this->set_data_set(message.get_data_set());
+    }
+}
+
+CGetResponse
+::CGetResponse(Message && message)
+: Response(message)
+{
+    this->_parse(message);
+    if(message.has_data_set())
+    {
+        this->set_data_set(std::move(message.get_data_set()));
+    }
+}
+
+CGetResponse
+::~CGetResponse()
+{
+    // Nothing to do.
+}
+
+void
+CGetResponse
+::_parse(Message const & message)
+{
     if(message.get_command_field() != Command::C_GET_RSP)
     {
         throw Exception("Message is not a C-GET-RSP");
     }
     this->set_command_field(message.get_command_field());
-    
+
     ODIL_MESSAGE_SET_OPTIONAL_FIELD_MACRO(
         message.get_command_set(), message_id, registry::MessageID, as_int)
     ODIL_MESSAGE_SET_OPTIONAL_FIELD_MACRO(
@@ -66,17 +104,6 @@ CGetResponse
     ODIL_MESSAGE_SET_OPTIONAL_FIELD_MACRO(
         message.get_command_set(), number_of_warning_sub_operations,
         registry::NumberOfWarningSuboperations, as_int)
-
-    if(message.has_data_set())
-    {
-        this->set_data_set(message.get_data_set());
-    }
-}
-
-CGetResponse
-::~CGetResponse()
-{
-    // Nothing to do.
 }
 
 }
diff --git a/src/odil/message/CGetResponse.h b/src/odil/message/CGetResponse.h
index cc01120..797db38 100644
--- a/src/odil/message/CGetResponse.h
+++ b/src/odil/message/CGetResponse.h
@@ -10,6 +10,7 @@
 #define _7b9819f1_d7a2_4898_a850_3ed6a61f08c6
 
 #include "odil/DataSet.h"
+#include "odil/odil.h"
 #include "odil/registry.h"
 #include "odil/message/Response.h"
 #include "odil/Value.h"
@@ -21,7 +22,7 @@ namespace message
 {
 
 /// @brief C-GET-RSP message.
-class CGetResponse: public Response
+class ODIL_API CGetResponse: public Response
 {
 public:
     /// @brief C-GET status codes, PS 3.4, C.4.3.1.4
@@ -51,12 +52,27 @@ public:
         DataSet const & dataset);
 
     /**
+     * @brief Create an get response with given Message ID, status,
+     * and data set.
+     */
+    CGetResponse(
+        Value::Integer message_id_being_responded_to, Value::Integer status,
+        DataSet && dataset);
+
+    /**
      * @brief Create a C-GET-RSP from a generic Message.
      *
      * Raise an exception if the Message does not contain a C-GET-RSP.
      */
     CGetResponse(Message const & message);
 
+    /**
+     * @brief Create a C-GET-RSP from a generic Message.
+     *
+     * Raise an exception if the Message does not contain a C-GET-RSP.
+     */
+    CGetResponse(Message && message);
+
     /// @brief Destructor.
     virtual ~CGetResponse();
 
@@ -72,6 +88,9 @@ public:
         number_of_failed_sub_operations, registry::NumberOfFailedSuboperations)
     ODIL_MESSAGE_OPTIONAL_FIELD_INTEGER_MACRO(
         number_of_warning_sub_operations, registry::NumberOfWarningSuboperations)
+
+private:
+    void _parse(Message const & message);
 };
 
 }
diff --git a/src/odil/message/CMoveRequest.cpp b/src/odil/message/CMoveRequest.cpp
index 15e81ab..fa00292 100644
--- a/src/odil/message/CMoveRequest.cpp
+++ b/src/odil/message/CMoveRequest.cpp
@@ -27,6 +27,50 @@ CMoveRequest
     DataSet const & dataset)
 : Request(message_id)
 {
+    this->_create(affected_sop_class_uid, priority, move_destination, dataset);
+    this->set_data_set(dataset);
+}
+
+CMoveRequest
+::CMoveRequest(
+    Value::Integer message_id, Value::String const & affected_sop_class_uid,
+    Value::Integer priority, Value::String const & move_destination,
+    DataSet && dataset)
+: Request(message_id)
+{
+    this->_create(affected_sop_class_uid, priority, move_destination, dataset);
+    this->set_data_set(std::move(dataset));
+}
+
+CMoveRequest
+::CMoveRequest(Message const & message)
+: Request(message)
+{
+    this->_parse(message);
+    this->set_data_set(message.get_data_set());
+}
+
+CMoveRequest
+::CMoveRequest(Message && message)
+: Request(message)
+{
+    this->_parse(message);
+    this->set_data_set(std::move(message.get_data_set()));
+}
+
+CMoveRequest
+::~CMoveRequest()
+{
+    // Nothing to do.
+}
+
+void
+CMoveRequest
+::_create(
+    Value::String const & affected_sop_class_uid,
+    Value::Integer priority, Value::String const & move_destination,
+    DataSet const & dataset)
+{
     this->set_command_field(Command::C_MOVE_RQ);
     this->set_affected_sop_class_uid(affected_sop_class_uid);
     this->set_priority(priority);
@@ -35,12 +79,11 @@ CMoveRequest
     {
         throw Exception("Data set is required");
     }
-    this->set_data_set(dataset);
 }
 
+void
 CMoveRequest
-::CMoveRequest(Message const & message)
-: Request(message)
+::_parse(Message const & message)
 {
     if(message.get_command_field() != Command::C_MOVE_RQ)
     {
@@ -60,13 +103,6 @@ CMoveRequest
     {
         throw Exception("Data set is required");
     }
-    this->set_data_set(message.get_data_set());
-}
-
-CMoveRequest
-::~CMoveRequest()
-{
-    // Nothing to do.
 }
 
 }
diff --git a/src/odil/message/CMoveRequest.h b/src/odil/message/CMoveRequest.h
index 4977e34..fc3437a 100644
--- a/src/odil/message/CMoveRequest.h
+++ b/src/odil/message/CMoveRequest.h
@@ -10,6 +10,7 @@
 #define _f6e243d2_6113_4fe3_8d04_3f034fc796bf
 
 #include "odil/DataSet.h"
+#include "odil/odil.h"
 #include "odil/registry.h"
 #include "odil/message/Request.h"
 #include "odil/Value.h"
@@ -21,7 +22,7 @@ namespace message
 {
 
 /// @brief C-MOVE-RQ message.
-class CMoveRequest: public Request
+class ODIL_API CMoveRequest: public Request
 {
 public:
     /**
@@ -34,12 +35,28 @@ public:
         DataSet const & dataset);
 
     /**
+     * @brief Create an move request with given Message ID,
+     * affected SOP class UID, priority, move destination, and data set.
+     */
+    CMoveRequest(
+        Value::Integer message_id, Value::String const & affected_sop_class_uid,
+        Value::Integer priority, Value::String const & move_destination,
+        DataSet && dataset);
+
+    /**
      * @brief Create a C-MOVE-RQ from a generic Message.
      *
      * Raise an exception if the Message does not contain a C-MOVE-RQ.
      */
     CMoveRequest(Message const & message);
 
+    /**
+     * @brief Create a C-MOVE-RQ from a generic Message.
+     *
+     * Raise an exception if the Message does not contain a C-MOVE-RQ.
+     */
+    CMoveRequest(Message && message);
+
     /// @brief Destructor.
     virtual ~CMoveRequest();
 
@@ -48,6 +65,13 @@ public:
     ODIL_MESSAGE_MANDATORY_FIELD_INTEGER_MACRO(priority, registry::Priority)
     ODIL_MESSAGE_MANDATORY_FIELD_STRING_MACRO(
         move_destination, registry::MoveDestination)
+
+private:
+    void _create(
+        Value::String const & affected_sop_class_uid,
+        Value::Integer priority, Value::String const & move_destination,
+        DataSet const & dataset);
+    void _parse(Message const & message);
 };
 
 }
diff --git a/src/odil/message/CMoveResponse.cpp b/src/odil/message/CMoveResponse.cpp
index 126fae8..54c8e5b 100644
--- a/src/odil/message/CMoveResponse.cpp
+++ b/src/odil/message/CMoveResponse.cpp
@@ -39,15 +39,53 @@ CMoveResponse
 }
 
 CMoveResponse
+::CMoveResponse(
+    Value::Integer message_id_being_responded_to, Value::Integer status,
+    DataSet && dataset)
+: Response(message_id_being_responded_to, status)
+{
+    this->set_command_field(Command::C_MOVE_RSP);
+    this->set_data_set(std::move(dataset));
+}
+
+CMoveResponse
 ::CMoveResponse(Message const & message)
 : Response(message)
 {
+    this->_parse(message);
+    if(message.has_data_set())
+    {
+        this->set_data_set(message.get_data_set());
+    }
+}
+
+CMoveResponse
+::CMoveResponse(Message && message)
+: Response(message)
+{
+    this->_parse(message);
+    if(message.has_data_set())
+    {
+        this->set_data_set(std::move(message.get_data_set()));
+    }
+}
+
+CMoveResponse
+::~CMoveResponse()
+{
+    // Nothing to do.
+}
+
+void
+CMoveResponse
+::_parse(Message const & message)
+{
     if(message.get_command_field() != Command::C_MOVE_RSP)
     {
         throw Exception("Message is not a C-MOVE-RSP");
     }
     this->set_command_field(message.get_command_field());
-    
+
     ODIL_MESSAGE_SET_OPTIONAL_FIELD_MACRO(
         message.get_command_set(), message_id, registry::MessageID, as_int)
     ODIL_MESSAGE_SET_OPTIONAL_FIELD_MACRO(
@@ -66,17 +104,6 @@ CMoveResponse
     ODIL_MESSAGE_SET_OPTIONAL_FIELD_MACRO(
         message.get_command_set(), number_of_warning_sub_operations,
         registry::NumberOfWarningSuboperations, as_int)
-
-    if(message.has_data_set())
-    {
-        this->set_data_set(message.get_data_set());
-    }
-}
-
-CMoveResponse
-::~CMoveResponse()
-{
-    // Nothing to do.
 }
 
 }
diff --git a/src/odil/message/CMoveResponse.h b/src/odil/message/CMoveResponse.h
index abded08..de9de2c 100644
--- a/src/odil/message/CMoveResponse.h
+++ b/src/odil/message/CMoveResponse.h
@@ -10,6 +10,7 @@
 #define _b245f6f2_50c3_4c7c_80e1_f03d9c831301
 
 #include "odil/DataSet.h"
+#include "odil/odil.h"
 #include "odil/registry.h"
 #include "odil/message/Response.h"
 #include "odil/Value.h"
@@ -21,7 +22,7 @@ namespace message
 {
 
 /// @brief C-MOVE-RSP message.
-class CMoveResponse: public Response
+class ODIL_API CMoveResponse: public Response
 {
 public:
     /// @brief C-MOVE status codes, PS 3.4, C.4.2.1.5
@@ -52,12 +53,27 @@ public:
         DataSet const & dataset);
 
     /**
+     * @brief Create an move response with given Message ID, status,
+     * and data set.
+     */
+    CMoveResponse(
+        Value::Integer message_id_being_responded_to, Value::Integer status,
+        DataSet && dataset);
+
+    /**
      * @brief Create a C-MOVE-RSP from a generic Message.
      *
      * Raise an exception if the Message does not contain a C-MOVE-RSP.
      */
     CMoveResponse(Message const & message);
 
+    /**
+     * @brief Create a C-MOVE-RSP from a generic Message.
+     *
+     * Raise an exception if the Message does not contain a C-MOVE-RSP.
+     */
+    CMoveResponse(Message && message);
+
     /// @brief Destructor.
     virtual ~CMoveResponse();
 
@@ -73,6 +89,9 @@ public:
         number_of_failed_sub_operations, registry::NumberOfFailedSuboperations)
     ODIL_MESSAGE_OPTIONAL_FIELD_INTEGER_MACRO(
         number_of_warning_sub_operations, registry::NumberOfWarningSuboperations)
+
+private:
+    void _parse(Message const & message);
 };
 
 }
diff --git a/src/odil/message/CStoreRequest.cpp b/src/odil/message/CStoreRequest.cpp
index 5cd1f31..a4773c3 100644
--- a/src/odil/message/CStoreRequest.cpp
+++ b/src/odil/message/CStoreRequest.cpp
@@ -28,6 +28,58 @@ CStoreRequest
     Value::Integer move_originator_message_id)
 : Request(message_id)
 {
+    this->_create(
+        affected_sop_class_uid, affected_sop_instance_uid, priority,
+        dataset, move_originator_ae_title, move_originator_message_id);
+    this->set_data_set(dataset);
+}
+
+CStoreRequest
+::CStoreRequest(
+    Value::Integer message_id, Value::String const & affected_sop_class_uid,
+    Value::String const & affected_sop_instance_uid,
+    Value::Integer priority, DataSet && dataset,
+    Value::String const & move_originator_ae_title,
+    Value::Integer move_originator_message_id)
+: Request(message_id)
+{
+    this->_create(
+        affected_sop_class_uid, affected_sop_instance_uid, priority,
+        dataset, move_originator_ae_title, move_originator_message_id);
+    this->set_data_set(std::move(dataset));
+}
+
+CStoreRequest
+::CStoreRequest(Message const & message)
+: Request(message)
+{
+    this->_parse(message);
+    this->set_data_set(message.get_data_set());
+}
+
+CStoreRequest
+::CStoreRequest(Message && message)
+: Request(message)
+{
+    this->_parse(message);
+    this->set_data_set(std::move(message.get_data_set()));
+}
+
+CStoreRequest
+::~CStoreRequest()
+{
+    // Nothing to do.
+}
+
+void
+CStoreRequest
+::_create(
+    Value::String const & affected_sop_class_uid,
+    Value::String const & affected_sop_instance_uid,
+    Value::Integer priority, DataSet const & dataset,
+    Value::String const & move_originator_ae_title,
+    Value::Integer move_originator_message_id)
+{
     this->set_command_field(Command::C_STORE_RQ);
     this->set_affected_sop_class_uid(affected_sop_class_uid);
     this->set_affected_sop_instance_uid(affected_sop_instance_uid);
@@ -42,12 +94,11 @@ CStoreRequest
     {
         throw Exception("Data set is required");
     }
-    this->set_data_set(dataset);
 }
 
+void
 CStoreRequest
-::CStoreRequest(Message const & message)
-: Request(message)
+::_parse(Message const & message)
 {
     if(message.get_command_field() != Command::C_STORE_RQ)
     {
@@ -72,13 +123,6 @@ CStoreRequest
     {
         throw Exception("Data set is required");
     }
-    this->set_data_set(message.get_data_set());
-}
-
-CStoreRequest
-::~CStoreRequest()
-{
-    // Nothing to do.
 }
 
 }
diff --git a/src/odil/message/CStoreRequest.h b/src/odil/message/CStoreRequest.h
index 0c08e26..f2d2559 100644
--- a/src/odil/message/CStoreRequest.h
+++ b/src/odil/message/CStoreRequest.h
@@ -12,6 +12,7 @@
 #include <string>
 
 #include "odil/DataSet.h"
+#include "odil/odil.h"
 #include "odil/registry.h"
 #include "odil/message/Request.h"
 #include "odil/Value.h"
@@ -23,7 +24,7 @@ namespace message
 {
 
 /// @brief C-STORE-RQ message.
-class CStoreRequest: public Request
+class ODIL_API CStoreRequest: public Request
 {
 public:
     /**
@@ -38,12 +39,30 @@ public:
         Value::Integer move_originator_message_id = -1);
 
     /**
+     * @brief Create an store request with given Message ID,
+     * affected SOP class UID, priority, and data set.
+     */
+    CStoreRequest(
+        Value::Integer message_id, Value::String const & affected_sop_class_uid,
+        Value::String const & affected_sop_instance_uid,
+        Value::Integer priority, DataSet && dataset,
+        Value::String const & move_originator_ae_title = "",
+        Value::Integer move_originator_message_id = -1);
+
+    /**
      * @brief Create a C-STORE-RQ from a generic Message.
      *
      * Raise an exception if the Message does not contain a C-STORE-RQ.
      */
     CStoreRequest(Message const & message);
 
+    /**
+     * @brief Create a C-STORE-RQ from a generic Message.
+     *
+     * Raise an exception if the Message does not contain a C-STORE-RQ.
+     */
+    CStoreRequest(Message && message);
+
     /// @brief Destructor.
     virtual ~CStoreRequest();
     
@@ -52,10 +71,20 @@ public:
     ODIL_MESSAGE_MANDATORY_FIELD_STRING_MACRO(
         affected_sop_instance_uid, registry::AffectedSOPInstanceUID)
     ODIL_MESSAGE_MANDATORY_FIELD_INTEGER_MACRO(priority, registry::Priority)
+
     ODIL_MESSAGE_OPTIONAL_FIELD_STRING_MACRO(
         move_originator_ae_title, registry::MoveOriginatorApplicationEntityTitle)
     ODIL_MESSAGE_OPTIONAL_FIELD_INTEGER_MACRO(
         move_originator_message_id, registry::MoveOriginatorMessageID)
+
+private:
+    void _create(
+        Value::String const & affected_sop_class_uid,
+        Value::String const & affected_sop_instance_uid,
+        Value::Integer priority, DataSet const & dataset,
+        Value::String const & move_originator_ae_title,
+        Value::Integer move_originator_message_id);
+    void _parse(Message const & message);
 };
 
 }
diff --git a/src/odil/message/CStoreResponse.h b/src/odil/message/CStoreResponse.h
index 7ed82d1..a6b23cd 100644
--- a/src/odil/message/CStoreResponse.h
+++ b/src/odil/message/CStoreResponse.h
@@ -9,6 +9,7 @@
 #ifndef _7e193624_081c_47dd_a011_986e96916ea9
 #define _7e193624_081c_47dd_a011_986e96916ea9
 
+#include "odil/odil.h"
 #include "odil/registry.h"
 #include "odil/message/Response.h"
 #include "odil/Value.h"
@@ -20,7 +21,7 @@ namespace message
 {
 
 /// @brief C-STORE-RSP message.
-class CStoreResponse: public Response
+class ODIL_API CStoreResponse: public Response
 {
 public:
     /// @brief C-STORE status codes, PS 3.4, B.2.3
diff --git a/src/odil/message/Cancellation.h b/src/odil/message/Cancellation.h
index 8290486..3efb611 100644
--- a/src/odil/message/Cancellation.h
+++ b/src/odil/message/Cancellation.h
@@ -10,6 +10,7 @@
 #define _97fc1bfc_4cff_40f2_a1ed_4550c71a0bda
 
 #include "odil/message/Message.h"
+#include "odil/odil.h"
 #include "odil/registry.h"
 #include "odil/Value.h"
 
@@ -20,7 +21,7 @@ namespace message
 {
 
 /// @brief Base class for cancellation messages.
-class Cancellation: public Message
+class ODIL_API Cancellation: public Message
 {
 public:
     /// @brief Create a response with given message id being responded to.
diff --git a/src/odil/message/Message.cpp b/src/odil/message/Message.cpp
index ff0836a..2eed751 100644
--- a/src/odil/message/Message.cpp
+++ b/src/odil/message/Message.cpp
@@ -37,6 +37,17 @@ Message
 }
 
 Message
+::Message(DataSet && command_set)
+: _command_set(std::move(command_set))
+{
+    if(!this->_command_set.has(registry::CommandDataSetType))
+    {
+        this->_command_set.add(registry::CommandDataSetType, VR::US);
+    }
+    this->_command_set.as_int(registry::CommandDataSetType) = { DataSetType::ABSENT };
+}
+
+Message
 ::Message(DataSet const & command_set, DataSet const & data_set)
 : _command_set(command_set)
 {
@@ -48,6 +59,17 @@ Message
 }
 
 Message
+::Message(DataSet && command_set, DataSet && data_set)
+: _command_set(std::move(command_set))
+{
+    if(!this->_command_set.has(registry::CommandDataSetType))
+    {
+        this->_command_set.add(registry::CommandDataSetType, VR::US);
+    }
+    this->set_data_set(std::move(data_set));
+}
+
+Message
 ::~Message()
 {
     // Nothing to do.
@@ -78,6 +100,17 @@ Message
     return this->_data_set;
 }
 
+DataSet &
+Message
+::get_data_set()
+{
+    if(!this->has_data_set())
+    {
+        throw Exception("No data set in message");
+    }
+    return this->_data_set;
+}
+
 void
 Message
 ::set_data_set(DataSet const & data_set)
@@ -88,6 +121,14 @@ Message
 
 void
 Message
+::set_data_set(DataSet && data_set)
+{
+    this->_data_set = std::move(data_set);
+    this->_command_set.as_int(registry::CommandDataSetType) = { DataSetType::PRESENT };
+}
+
+void
+Message
 ::delete_data_set()
 {
     this->_command_set.as_int(registry::CommandDataSetType) = { DataSetType::ABSENT };
diff --git a/src/odil/message/Message.h b/src/odil/message/Message.h
index 5a74391..f4c9e75 100644
--- a/src/odil/message/Message.h
+++ b/src/odil/message/Message.h
@@ -10,6 +10,7 @@
 #define _dcfa5213_ad7e_4194_8b4b_e630aa0df2e8
 
 #include "odil/DataSet.h"
+#include "odil/odil.h"
 #include "odil/registry.h"
 #include "odil/Value.h"
 
@@ -72,7 +73,7 @@ namespace message
 /**
  * @brief Base class for all DIMSE messages.
  */
-class Message
+class ODIL_API Message
 {
 public:
     struct Command
@@ -142,8 +143,14 @@ public:
     Message(DataSet const & command_set);
 
     /// @brief Create a message from existing data.
+    Message(DataSet && command_set);
+
+    /// @brief Create a message from existing data.
     Message(DataSet const & command_set, DataSet const & data_set);
 
+    /// @brief Create a message from existing data.
+    Message(DataSet && command_set, DataSet && data_set);
+
     /// @brief Destructor;
     virtual ~Message();
     
@@ -158,10 +165,19 @@ public:
      * data set is present.
      */
     DataSet const & get_data_set() const;
+
+    /**
+     * @brief Return the data set of the message, raise an exception if no
+     * data set is present.
+     */
+    DataSet & get_data_set();
     
     /// @brief Set the data set of the message.
     void set_data_set(DataSet const & data_set);
 
+    /// @brief Set the data set of the message.
+    void set_data_set(DataSet && data_set);
+
     /// @brief Delete the data set in this message.
     void delete_data_set();
     
diff --git a/src/odil/message/NCreateRequest.cpp b/src/odil/message/NCreateRequest.cpp
new file mode 100644
index 0000000..5e9f79b
--- /dev/null
+++ b/src/odil/message/NCreateRequest.cpp
@@ -0,0 +1,51 @@
+/*************************************************************************
+ * odil - Copyright (C) Universite de Strasbourg
+ * Distributed under the terms of the CeCILL-B license, as published by
+ * the CEA-CNRS-INRIA. Refer to the LICENSE file or to
+ * http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html
+ * for details.
+ ************************************************************************/
+
+#include "NCreateRequest.h"
+
+#include "odil/message/Request.h"
+#include "odil/registry.h"
+#include "odil/Value.h"
+
+namespace odil
+{
+
+namespace message
+{
+
+NCreateRequest
+::NCreateRequest(Value::Integer message_id, const Value::String &affected_sop_class_uid)
+: Request(message_id)
+{
+    this->set_command_field(Command::N_CREATE_RQ);
+    this->set_affected_sop_class_uid( affected_sop_class_uid ) ;
+}
+
+NCreateRequest
+::NCreateRequest(Message const & message)
+: Request(message)
+{
+    if(message.get_command_field() != Command::N_CREATE_RQ)
+    {
+        throw Exception("Message is not a N-CREATE-RQ");
+    }
+    this->set_command_field(message.get_command_field());
+
+    this->set_affected_sop_class_uid(
+        message.get_command_set().as_string( registry::AffectedSOPClassUID, 0));
+}
+
+NCreateRequest
+::~NCreateRequest()
+{
+    // Nothing to do.
+}
+
+}
+
+}
diff --git a/src/odil/message/NCreateRequest.h b/src/odil/message/NCreateRequest.h
new file mode 100644
index 0000000..b2f85ac
--- /dev/null
+++ b/src/odil/message/NCreateRequest.h
@@ -0,0 +1,63 @@
+/*************************************************************************
+ * odil - Copyright (C) Universite de Strasbourg
+ * Distributed under the terms of the CeCILL-B license, as published by
+ * the CEA-CNRS-INRIA. Refer to the LICENSE file or to
+ * http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html
+ * for details.
+ ************************************************************************/
+
+#ifndef _9a741b7f_0254_4eac_9bbd_04b719c1f86a
+#define _9a741b7f_0254_4eac_9bbd_04b719c1f86a
+
+#include "odil/message/Request.h"
+#include "odil/odil.h"
+#include "odil/registry.h"
+#include "odil/Value.h"
+
+namespace odil
+{
+
+namespace message
+{
+
+/// @brief N-Create-RQ message.
+class ODIL_API NCreateRequest: public Request
+{
+public:
+    /**
+     * @brief Create an NCreate request with given Message ID and
+     * affected SOP class UID.
+     */
+    NCreateRequest(
+        Value::Integer message_id,
+        Value::String const & affected_sop_class_uid);
+
+    /**
+     * @brief Create a N-Create-RQ from a generic Message.
+     *
+     * Raise an exception if the Message does not contain a N-Create-RQ.
+     */
+    NCreateRequest(Message const & message);
+
+    /// @brief Destructor.
+    virtual ~NCreateRequest();
+    
+    ODIL_MESSAGE_MANDATORY_FIELD_INTEGER_MACRO(
+        message_id, registry::MessageID)
+
+    ODIL_MESSAGE_MANDATORY_FIELD_STRING_MACRO(
+            affected_sop_class_uid, registry::AffectedSOPClassUID)
+
+    ODIL_MESSAGE_OPTIONAL_FIELD_INTEGER_MACRO(
+            affected_sop_instance_uid, registry::AffectedSOPInstanceUID)
+
+    ODIL_MESSAGE_OPTIONAL_FIELD_STRING_MACRO(
+            attribute_list, registry::AttributeIdentifierList)
+
+};
+
+}
+
+}
+
+#endif // _9a741b7f_0254_4eac_9bbd_04b719c1f86a
diff --git a/src/odil/message/NCreateResponse.cpp b/src/odil/message/NCreateResponse.cpp
new file mode 100644
index 0000000..4516794
--- /dev/null
+++ b/src/odil/message/NCreateResponse.cpp
@@ -0,0 +1,53 @@
+/*************************************************************************
+ * odil - Copyright (C) Universite de Strasbourg
+ * Distributed under the terms of the CeCILL-B license, as published by
+ * the CEA-CNRS-INRIA. Refer to the LICENSE file or to
+ * http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html
+ * for details.
+ ************************************************************************/
+
+#include "NCreateResponse.h"
+
+#include "odil/Exception.h"
+#include "odil/registry.h"
+#include "odil/message/Response.h"
+#include "odil/Value.h"
+
+namespace odil
+{
+
+namespace message
+{
+
+NCreateResponse
+::NCreateResponse(const Value::Integer &message_id_being_responded_to, const Value::Integer &status,
+    Value::String const & affected_sop_class_uid)
+: Response(message_id_being_responded_to, status)
+{
+    this->set_command_field(Command::N_CREATE_RSP);
+    this->set_affected_sop_class_uid(affected_sop_class_uid);
+}
+
+NCreateResponse
+::NCreateResponse(Message const & message)
+: Response(message)
+{
+    if(message.get_command_field() != Command::N_CREATE_RSP)
+    {
+        throw Exception("Message is not a N-CREATE-RSP");
+    }
+    this->set_command_field(message.get_command_field());
+    
+    this->set_affected_sop_class_uid(
+        message.get_command_set().as_string(registry::AffectedSOPClassUID, 0));
+}
+
+NCreateResponse
+::~NCreateResponse()
+{
+    // Nothing to do.
+}
+
+}
+
+}
diff --git a/src/odil/message/NCreateResponse.h b/src/odil/message/NCreateResponse.h
new file mode 100644
index 0000000..f789960
--- /dev/null
+++ b/src/odil/message/NCreateResponse.h
@@ -0,0 +1,61 @@
+/*************************************************************************
+ * odil - Copyright (C) Universite de Strasbourg
+ * Distributed under the terms of the CeCILL-B license, as published by
+ * the CEA-CNRS-INRIA. Refer to the LICENSE file or to
+ * http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html
+ * for details.
+ ************************************************************************/
+ 
+#ifndef _4f465ff9_8cd7_47cb_afc5_51461124bb01
+#define _4f465ff9_8cd7_47cb_afc5_51461124bb01
+
+#include "odil/odil.h"
+#include "odil/registry.h"
+#include "odil/message/Response.h"
+#include "odil/Value.h"
+
+namespace odil
+{
+
+namespace message
+{
+
+/// @brief N-Create-RSP message.
+class ODIL_API NCreateResponse: public Response
+{
+public:
+    /**
+     * @brief Create an NCreate response with given Message ID and
+     * affected SOP class UID.
+     */
+    NCreateResponse(
+        Value::Integer const & message_id_being_responded_to
+            , Value::Integer const & status
+            , Value::String const & affected_sop_class_uid);
+
+    /**
+     * @brief Create a N-Create-RSP from a generic Message.
+     *
+     * Raise an exception if the Message does not contain a N-Create-RSP.
+     */
+    NCreateResponse(Message const & message);
+
+    /// @brief Destructor.
+    virtual ~NCreateResponse();
+    
+    ODIL_MESSAGE_MANDATORY_FIELD_INTEGER_MACRO(
+        message_id_being_responded_to, registry::MessageIDBeingRespondedTo)
+
+    ODIL_MESSAGE_OPTIONAL_FIELD_STRING_MACRO(
+        affected_sop_class_uid, registry::AffectedSOPClassUID)
+
+
+
+
+};
+
+}
+
+}
+
+#endif // _4f465ff9_8cd7_47cb_afc5_51461124bb01
diff --git a/src/odil/message/NSetRequest.cpp b/src/odil/message/NSetRequest.cpp
new file mode 100644
index 0000000..87aaba9
--- /dev/null
+++ b/src/odil/message/NSetRequest.cpp
@@ -0,0 +1,93 @@
+/*************************************************************************
+ * odil - Copyright (C) Universite de Strasbourg
+ * Distributed under the terms of the CeCILL-B license, as published by
+ * the CEA-CNRS-INRIA. Refer to the LICENSE file or to
+ * http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html
+ * for details.
+ ************************************************************************/
+
+#include "NSetRequest.h"
+
+#include <string>
+
+#include "odil/Exception.h"
+#include "odil/VR.h"
+
+namespace odil
+{
+
+namespace message
+{
+
+NSetRequest
+::NSetRequest(
+        Value::Integer message_id,
+        Value::String const & requested_sop_class_uid,
+        Value::String const & requested_sop_instance_uid,
+        DataSet const & dataset)
+: Request(message_id)
+{
+    this->set_command_field( ::odil::message::Message::Command::N_SET_RQ);
+    this->set_requested_sop_class_uid(requested_sop_class_uid);
+    this->set_requested_sop_instance_uid(requested_sop_instance_uid);
+
+    if(dataset.empty() )
+    {
+        throw Exception("Data set is required");
+    }
+    this->set_data_set(dataset);
+}
+
+/*
+NSetRequest
+::NSetRequest(Value::Integer message_id,
+              Value::String const & requested_sop_class_uid,
+              Value::String const & requested_sop_instance_uid,
+              DataSet const & dataset
+              )
+    : Request(message_id)
+{
+    this->set_command_field( ::odil::message::Message::Command::N_SET_RQ);
+    this->set_requested_sop_class_uid(requested_sop_class_uid);
+    this->set_requested_sop_instance_uid(requested_sop_instance_uid);
+
+    if( dataset.empty() )
+    {
+        throw Exception("Data set is required");
+    }
+    this->set_data_set(dataset);
+}
+*/
+
+NSetRequest
+::NSetRequest(Message const & message)
+: Request(message)
+{
+    if(message.get_command_field() != Command::N_SET_RQ)
+    {
+        throw Exception("Message is not a N-SET-RQ");
+    }
+
+    this->set_command_field(message.get_command_field());
+
+    this->set_requested_sop_class_uid(
+        message.get_command_set().as_string(registry::RequestedSOPClassUID, 0));
+    this->set_requested_sop_instance_uid(
+                message.get_command_set().as_string(registry::RequestedSOPInstanceUID, 0));
+
+    if(!message.has_data_set() || message.get_data_set().empty() )
+    {
+        throw Exception("Data set is required");
+    }
+    this->set_data_set(message.get_data_set());
+}
+
+NSetRequest
+::~NSetRequest()
+{
+    // Nothing to do.
+}
+
+}
+
+}
diff --git a/src/odil/message/NSetRequest.h b/src/odil/message/NSetRequest.h
new file mode 100644
index 0000000..72a78e2
--- /dev/null
+++ b/src/odil/message/NSetRequest.h
@@ -0,0 +1,59 @@
+/*************************************************************************
+ * odil - Copyright (C) Universite de Strasbourg
+ * Distributed under the terms of the CeCILL-B license, as published by
+ * the CEA-CNRS-INRIA. Refer to the LICENSE file or to
+ * http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html
+ * for details.
+ ************************************************************************/
+
+#ifndef _8125ab8d_bf37_4116_8ca8_93151ba022a8
+#define _8125ab8d_bf37_4116_8ca8_93151ba022a8
+
+#include "odil/message/Request.h"
+#include "odil/odil.h"
+#include "odil/registry.h"
+#include "odil/Value.h"
+
+namespace odil
+{
+
+namespace message
+{
+
+/// @brief N-Set-RQ message.
+class ODIL_API NSetRequest: public Request
+{
+public:
+    /**
+     * @brief Create a N-Set-RQ from a generic Message.
+     *
+     * Raise an exception if the Message does not contain a N-Set-RQ.
+     */
+    NSetRequest(Message const & message);
+
+    /**
+     * @brief Create an NSet request with given Message ID and
+     * requested SOP class UID and SOP Instance UID.
+     */
+    NSetRequest(
+            Value::Integer message_id,
+            Value::String const & requested_sop_class_uid,
+            Value::String const & requested_sop_instance_uid,
+            DataSet const & dataset //will be used to describe list of modifications to do.
+            );
+
+    /// @brief Destructor.
+    virtual ~NSetRequest();
+
+    ODIL_MESSAGE_MANDATORY_FIELD_STRING_MACRO(
+        requested_sop_class_uid, registry::RequestedSOPClassUID)
+    ODIL_MESSAGE_MANDATORY_FIELD_STRING_MACRO(
+        requested_sop_instance_uid, registry::RequestedSOPInstanceUID)
+
+};
+
+}
+
+}
+
+#endif // _8125ab8d_bf37_4116_8ca8_93151ba022a8
diff --git a/src/odil/message/NSetResponse.cpp b/src/odil/message/NSetResponse.cpp
new file mode 100644
index 0000000..d55d45f
--- /dev/null
+++ b/src/odil/message/NSetResponse.cpp
@@ -0,0 +1,61 @@
+/*************************************************************************
+ * odil - Copyright (C) Universite de Strasbourg
+ * Distributed under the terms of the CeCILL-B license, as published by
+ * the CEA-CNRS-INRIA. Refer to the LICENSE file or to
+ * http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html
+ * for details.
+ ************************************************************************/
+
+#include "NSetResponse.h"
+
+#include "odil/Exception.h"
+#include "odil/registry.h"
+#include "odil/message/Response.h"
+#include "odil/Value.h"
+
+namespace odil
+{
+
+namespace message
+{
+
+NSetResponse
+::NSetResponse(const Value::Integer &message_id_being_responded_to
+               , const Value::Integer & status
+        , Value::String const & requested_sop_class_uid
+        , Value::String const & requested_sop_instance_uid)
+: Response(message_id_being_responded_to, status)
+{
+    this->set_command_field(Command::N_SET_RSP);
+    this->set_affected_sop_class_uid(requested_sop_class_uid);
+    this->set_affected_sop_instance_uid(requested_sop_instance_uid );
+
+}
+
+NSetResponse
+::NSetResponse(Message const & message)
+: Response(message )
+{
+    if(message.get_command_field() != Command::N_SET_RSP)
+    {
+        throw Exception("Message is not a N-SET-RSP");
+    }
+
+    this->set_command_field(message.get_command_field());
+
+    this->set_affected_sop_class_uid(
+        message.get_command_set().as_string(registry::RequestedSOPClassUID, 0));
+
+    this->set_affected_sop_instance_uid(
+                message.get_command_set().as_string(registry::RequestedSOPInstanceUID, 0));
+}
+
+NSetResponse
+::~NSetResponse()
+{
+    // Nothing to do.
+}
+
+}
+
+}
diff --git a/src/odil/message/NSetResponse.h b/src/odil/message/NSetResponse.h
new file mode 100644
index 0000000..71a452f
--- /dev/null
+++ b/src/odil/message/NSetResponse.h
@@ -0,0 +1,58 @@
+/*************************************************************************
+ * odil - Copyright (C) Universite de Strasbourg
+ * Distributed under the terms of the CeCILL-B license, as published by
+ * the CEA-CNRS-INRIA. Refer to the LICENSE file or to
+ * http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html
+ * for details.
+ ************************************************************************/
+
+#ifndef _5d350855_531e_405a_a221_af3b0e720fd1
+#define _5d350855_531e_405a_a221_af3b0e720fd1
+
+#include "odil/registry.h"
+#include "odil/message/Response.h"
+#include "odil/odil.h"
+#include "odil/Value.h"
+
+namespace odil
+{
+
+namespace message
+{
+
+/// @brief N-Set-RSP message.
+class ODIL_API NSetResponse: public Response
+{
+public:
+    /**
+     * @brief Create an NSet response with given Message ID and
+     * affected SOP class UID.
+     */
+    NSetResponse(
+            Value::Integer const & message_id_being_responded_to,
+            Value::Integer const & status,
+            Value::String const & requested_sop_class_uid  ,
+            Value::String const & requested_sop_instance_uid
+            );
+
+    /**
+     * @brief Create a N-Set-RSP from a generic Message.
+     *
+     * Raise an exception if the Message does not contain a N-Set-RSP.
+     */
+    NSetResponse(Message const & message);
+
+    /// @brief Destructor.
+    virtual ~NSetResponse();
+    
+    ODIL_MESSAGE_MANDATORY_FIELD_INTEGER_MACRO( status, registry::Status )
+
+    ODIL_MESSAGE_OPTIONAL_FIELD_STRING_MACRO(affected_sop_class_uid, registry::AffectedSOPClassUID)
+
+};
+
+}
+
+}
+
+#endif // _5d350855_531e_405a_a221_af3b0e720fd1
diff --git a/src/odil/message/Request.h b/src/odil/message/Request.h
index 63aba04..29344a1 100644
--- a/src/odil/message/Request.h
+++ b/src/odil/message/Request.h
@@ -10,6 +10,7 @@
 #define _8d06a300_6aee_4d1f_bf10_ecdf4916ae9f
 
 #include "odil/message/Message.h"
+#include "odil/odil.h"
 #include "odil/registry.h"
 #include "odil/Value.h"
 
@@ -20,7 +21,7 @@ namespace message
 {
 
 /// @brief Base class for all DIMSE request messages.
-class Request: public Message
+class ODIL_API Request: public Message
 {
 public:
     /// @brief Create a request with given Message ID.
diff --git a/src/odil/message/Response.h b/src/odil/message/Response.h
index fe1dd80..eb8c672 100644
--- a/src/odil/message/Response.h
+++ b/src/odil/message/Response.h
@@ -10,6 +10,7 @@
 #define _0dd2e31e_212a_494a_a8d3_93b235336658
 
 #include "odil/message/Message.h"
+#include "odil/odil.h"
 #include "odil/registry.h"
 #include "odil/Value.h"
 
@@ -20,7 +21,7 @@ namespace message
 {
 
 /// @brief Base class for all DIMSE response messages.
-class Response: public Message
+class ODIL_API Response: public Message
 {
 public:
     /// @brief General status codes, from PS3.7, C
diff --git a/src/odil/odil.h b/src/odil/odil.h
index dcecb68..e2ddabb 100644
--- a/src/odil/odil.h
+++ b/src/odil/odil.h
@@ -3,6 +3,11 @@
 #ifdef WIN32
 #  define EXPORT_DYNAMIC_LIBRARY __declspec(dllexport)
 #  define IMPORT_DYNAMIC_LIBRARY __declspec(dllimport)
+// disable warning: 'identifier': class 'type' needs to have dll-interface to be
+//   used by clients of class 'type2'
+// disable warning: non - DLL-interface classkey 'identifier' used as base for
+//   DLL-interface classkey 'identifier'
+#pragma warning( disable : 4251 4275 )
 #else // WIN32
 #  define EXPORT_DYNAMIC_LIBRARY
 #  define IMPORT_DYNAMIC_LIBRARY
@@ -12,4 +17,4 @@
 #  define ODIL_API EXPORT_DYNAMIC_LIBRARY
 #else // BUILDING_ODIL
 #  define ODIL_API IMPORT_DYNAMIC_LIBRARY
-#endif // BUILDING_ODIL
\ No newline at end of file
+#endif // BUILDING_ODIL
diff --git a/src/odil/pdu/AAbort.h b/src/odil/pdu/AAbort.h
index 7da2f07..3013e5f 100644
--- a/src/odil/pdu/AAbort.h
+++ b/src/odil/pdu/AAbort.h
@@ -11,6 +11,7 @@
 
 #include <istream>
 
+#include "odil/odil.h"
 #include "odil/pdu/Object.h"
 
 namespace odil
@@ -20,7 +21,7 @@ namespace pdu
 {
 
 /// @brief A-ABORT PDU, cf. PS 3.8, 9.3.8.
-class AAbort: public Object
+class ODIL_API AAbort: public Object
 {
 public:
     /// @brief Constructor.
diff --git a/src/odil/pdu/AAssociate.h b/src/odil/pdu/AAssociate.h
index ed18e27..d45463d 100644
--- a/src/odil/pdu/AAssociate.h
+++ b/src/odil/pdu/AAssociate.h
@@ -13,6 +13,7 @@
 #include <istream>
 #include <string>
 
+#include "odil/odil.h"
 #include "odil/pdu/ApplicationContext.h"
 #include "odil/pdu/Object.h"
 #include "odil/pdu/UserInformation.h"
@@ -24,7 +25,7 @@ namespace pdu
 {
 
 /// @brief A-ASSOCIATE-RQ and A-ASSOCIATE-AC PDU, cf. PS 3.8, 9.3.2 and 9.3.3.
-class AAssociate: public Object
+class ODIL_API AAssociate: public Object
 {
 public:
     /// @brief Constructor.
diff --git a/src/odil/pdu/AAssociateAC.h b/src/odil/pdu/AAssociateAC.h
index 3982bdd..979ea90 100644
--- a/src/odil/pdu/AAssociateAC.h
+++ b/src/odil/pdu/AAssociateAC.h
@@ -11,6 +11,7 @@
 
 #include <vector>
 
+#include "odil/odil.h"
 #include "odil/pdu/AAssociate.h"
 #include "odil/pdu/PresentationContextAC.h"
 
@@ -21,7 +22,7 @@ namespace pdu
 {
 
 /// @brief A-ASSOCIATE-AC PDU, cf. PS 3.8, 9.3.3.
-class AAssociateAC: public AAssociate
+class ODIL_API AAssociateAC: public AAssociate
 {
 public:
     /// @brief Constructor.
diff --git a/src/odil/pdu/AAssociateRJ.h b/src/odil/pdu/AAssociateRJ.h
index 2886113..6455cdc 100644
--- a/src/odil/pdu/AAssociateRJ.h
+++ b/src/odil/pdu/AAssociateRJ.h
@@ -9,6 +9,7 @@
 #ifndef _3980566c_9185_40a2_8e7d_6286c2cd1959
 #define _3980566c_9185_40a2_8e7d_6286c2cd1959
 
+#include "odil/odil.h"
 #include "odil/pdu/Object.h"
 
 namespace odil
@@ -18,7 +19,7 @@ namespace pdu
 {
 
 /// @brief A-ASSOCIATE-RJ PDU, cf. PS 3.8, 9.3.4.
-class AAssociateRJ: public Object
+class ODIL_API AAssociateRJ: public Object
 {
 public:
     /// @brief Constructor.
diff --git a/src/odil/pdu/AAssociateRQ.h b/src/odil/pdu/AAssociateRQ.h
index 98b8809..a9c0068 100644
--- a/src/odil/pdu/AAssociateRQ.h
+++ b/src/odil/pdu/AAssociateRQ.h
@@ -11,6 +11,7 @@
 
 #include <vector>
 
+#include "odil/odil.h"
 #include "odil/pdu/AAssociate.h"
 #include "odil/pdu/PresentationContextRQ.h"
 
@@ -21,7 +22,7 @@ namespace pdu
 {
 
 /// @brief A-ASSOCIATE-RQ, cf. PS 3.8, 9.3.2
-class AAssociateRQ: public AAssociate
+class ODIL_API AAssociateRQ: public AAssociate
 {
 public:
     /// @brief Constructor.
diff --git a/src/odil/pdu/AReleaseRP.h b/src/odil/pdu/AReleaseRP.h
index d9f0cc2..1d0628e 100644
--- a/src/odil/pdu/AReleaseRP.h
+++ b/src/odil/pdu/AReleaseRP.h
@@ -11,6 +11,7 @@
 
 #include <istream>
 
+#include "odil/odil.h"
 #include "odil/pdu/Object.h"
 
 namespace odil
@@ -20,7 +21,7 @@ namespace pdu
 {
 
 /// @brief A-RELEASE-RP PDU, cf. PS 3.8, 9.3.7.
-class AReleaseRP: public Object
+class ODIL_API AReleaseRP: public Object
 {
 public:
     /// @brief Constructor.
diff --git a/src/odil/pdu/AReleaseRQ.h b/src/odil/pdu/AReleaseRQ.h
index 87c2fcf..71e28fe 100644
--- a/src/odil/pdu/AReleaseRQ.h
+++ b/src/odil/pdu/AReleaseRQ.h
@@ -11,6 +11,7 @@
 
 #include <istream>
 
+#include "odil/odil.h"
 #include "odil/pdu/Object.h"
 
 namespace odil
@@ -20,7 +21,7 @@ namespace pdu
 {
 
 /// @brief A-RELEASE-RQ PDU, cf. PS 3.8, 9.3.6.
-class AReleaseRQ: public Object
+class ODIL_API AReleaseRQ: public Object
 {
 public:
     /// @brief Constructor.
diff --git a/src/odil/pdu/ApplicationContext.h b/src/odil/pdu/ApplicationContext.h
index 7d7260a..6a36202 100644
--- a/src/odil/pdu/ApplicationContext.h
+++ b/src/odil/pdu/ApplicationContext.h
@@ -12,6 +12,7 @@
 #include <istream>
 #include <string>
 
+#include "odil/odil.h"
 #include "odil/pdu/Object.h"
 
 namespace odil
@@ -23,7 +24,7 @@ namespace pdu
 /**
  * @brief Application Context item, (PS 3.8, 9.3.2.1).
  */
-class ApplicationContext: public Object
+class ODIL_API ApplicationContext: public Object
 {
 public:
     /// @brief Create an Application Context.
diff --git a/src/odil/pdu/AsynchronousOperationsWindow.h b/src/odil/pdu/AsynchronousOperationsWindow.h
index 608246b..abae2ab 100644
--- a/src/odil/pdu/AsynchronousOperationsWindow.h
+++ b/src/odil/pdu/AsynchronousOperationsWindow.h
@@ -12,6 +12,7 @@
 #include <cstdint>
 #include <istream>
 
+#include "odil/odil.h"
 #include "odil/pdu/Object.h"
 
 namespace odil
@@ -21,7 +22,7 @@ namespace pdu
 {
 
 /// @brief Asynchronous Operations Window Sub-Item (PS 3.7, D.3.3.3.1 and D.3.3.3.2).
-class AsynchronousOperationsWindow: public Object
+class ODIL_API AsynchronousOperationsWindow: public Object
 {
 public:
     /// @brief Item type.
diff --git a/src/odil/pdu/ImplementationClassUID.h b/src/odil/pdu/ImplementationClassUID.h
index 3a3a41f..aaf2cb0 100644
--- a/src/odil/pdu/ImplementationClassUID.h
+++ b/src/odil/pdu/ImplementationClassUID.h
@@ -13,6 +13,7 @@
 #include <istream>
 #include <string>
 
+#include "odil/odil.h"
 #include "odil/pdu/Object.h"
 
 namespace odil
@@ -22,7 +23,7 @@ namespace pdu
 {
 
 /// @brief Implementation Class UID Sub-Item (PS 3.7, D.3.3.2.1 and D.3.3.2.2).
-class ImplementationClassUID: public Object
+class ODIL_API ImplementationClassUID: public Object
 {
 public:
     /// @brief Item type.
diff --git a/src/odil/pdu/ImplementationVersionName.h b/src/odil/pdu/ImplementationVersionName.h
index 1218709..827a682 100644
--- a/src/odil/pdu/ImplementationVersionName.h
+++ b/src/odil/pdu/ImplementationVersionName.h
@@ -13,6 +13,7 @@
 #include <istream>
 #include <string>
 
+#include "odil/odil.h"
 #include "odil/pdu/Object.h"
 
 namespace odil
@@ -22,7 +23,7 @@ namespace pdu
 {
 
 /// @brief Implementation Version Name Sub-Item (PS 3.7, D.3.3.2.3 and D.3.3.2.4).
-class ImplementationVersionName: public Object
+class ODIL_API ImplementationVersionName: public Object
 {
 public:
     /// @brief Item type.
diff --git a/src/odil/pdu/Item.h b/src/odil/pdu/Item.h
index 37cb44f..b6444ec 100644
--- a/src/odil/pdu/Item.h
+++ b/src/odil/pdu/Item.h
@@ -16,6 +16,8 @@
 #include <utility>
 #include <vector>
 
+#include "odil/odil.h"
+
 namespace odil
 {
 
@@ -23,11 +25,11 @@ namespace pdu
 {
 
 /// @brief A sequence of fields forming a full PDU or a part of it.
-class Item
+class ODIL_API Item
 {
 public:
     /// @brief Generic field.
-    class Field
+    class ODIL_API Field
     {
     public:
         /// @brief Possible types stored in the field.
@@ -256,6 +258,7 @@ private:
 
 // No operator>> since we need explicit names and types.
 
+ODIL_API
 std::ostream &
 operator<<(std::ostream & stream, Item const & item);
 
diff --git a/src/odil/pdu/MaximumLength.h b/src/odil/pdu/MaximumLength.h
index cc01877..a1d6899 100644
--- a/src/odil/pdu/MaximumLength.h
+++ b/src/odil/pdu/MaximumLength.h
@@ -12,6 +12,7 @@
 #include <cstdint>
 #include <istream>
 
+#include "odil/odil.h"
 #include "odil/pdu/Object.h"
 
 namespace odil
@@ -21,7 +22,7 @@ namespace pdu
 {
 
 /// @brief Maximum Length Sub-Item Structure (PS 3.8, D.1).
-class MaximumLength: public Object
+class ODIL_API MaximumLength: public Object
 {
 public:
     /// @brief Item type.
diff --git a/src/odil/pdu/Object.h b/src/odil/pdu/Object.h
index db1bfcd..0584465 100644
--- a/src/odil/pdu/Object.h
+++ b/src/odil/pdu/Object.h
@@ -10,6 +10,8 @@
 #define _da2270e3_d393_415a_9c5c_6253152ed9da
 
 #include <ostream>
+
+#include "odil/odil.h"
 #include "odil/pdu/Item.h"
 
 namespace odil
@@ -22,7 +24,7 @@ namespace pdu
  * @brief Base class for all PDU-related high-level objects (PDU, items and
  * sub-items).
  */
-class Object
+class ODIL_API Object
 {
 public:
     /// @brief Destructor, makes the type polymorphic.
@@ -45,6 +47,7 @@ protected:
 };
 
 /// @brief Dump the PDU-object in its binary form.
+ODIL_API
 std::ostream &
 operator<<(std::ostream & stream, Object const & object);
 
diff --git a/src/odil/pdu/PDataTF.h b/src/odil/pdu/PDataTF.h
index cc3abbf..2bc0201 100644
--- a/src/odil/pdu/PDataTF.h
+++ b/src/odil/pdu/PDataTF.h
@@ -13,6 +13,7 @@
 #include <istream>
 #include <vector>
 
+#include "odil/odil.h"
 #include "odil/pdu/Object.h"
 
 namespace odil
@@ -22,10 +23,10 @@ namespace pdu
 {
 
 /// @brief P-DATA-TF PDU, cf. PS 3.8, 9.3.5.
-class PDataTF: public Object
+class ODIL_API PDataTF: public Object
 {
 public:
-    class PresentationDataValueItem: public Object
+    class ODIL_API PresentationDataValueItem: public Object
     {
     public:
         PresentationDataValueItem(
diff --git a/src/odil/pdu/PresentationContext.h b/src/odil/pdu/PresentationContext.h
index 2cf07c8..1174641 100644
--- a/src/odil/pdu/PresentationContext.h
+++ b/src/odil/pdu/PresentationContext.h
@@ -14,6 +14,7 @@
 #include <string>
 #include <vector>
 
+#include "odil/odil.h"
 #include "odil/pdu/Object.h"
 
 namespace odil
@@ -26,7 +27,7 @@ namespace pdu
  * @brief Presentation Context item, either for a A-ASSOCIATE-RQ PDU (PS 3.8,
  * 9.3.2.2) or for a A-ASSOCIATE-AC PDU (PS 3.8, 9.3.3.2).
  */
-class PresentationContext: public Object
+class ODIL_API PresentationContext: public Object
 {
 public:
     /// @brief Constructor
diff --git a/src/odil/pdu/PresentationContextAC.h b/src/odil/pdu/PresentationContextAC.h
index e04108e..75c156e 100644
--- a/src/odil/pdu/PresentationContextAC.h
+++ b/src/odil/pdu/PresentationContextAC.h
@@ -13,6 +13,7 @@
 #include <istream>
 #include <string>
 
+#include "odil/odil.h"
 #include "odil/pdu/PresentationContext.h"
 
 namespace odil
@@ -22,7 +23,7 @@ namespace pdu
 {
 
 /// @brief Presentation Context item for a A-ASSOCIATE-AC PDU (PS 3.8, 9.3.3.2).
-class PresentationContextAC: public PresentationContext
+class ODIL_API PresentationContextAC: public PresentationContext
 {
 public:
     /// @brief Constructor.
diff --git a/src/odil/pdu/PresentationContextRQ.h b/src/odil/pdu/PresentationContextRQ.h
index 4582601..87377a8 100644
--- a/src/odil/pdu/PresentationContextRQ.h
+++ b/src/odil/pdu/PresentationContextRQ.h
@@ -14,6 +14,7 @@
 #include <string>
 #include <vector>
 
+#include "odil/odil.h"
 #include "odil/pdu/PresentationContext.h"
 
 namespace odil
@@ -23,7 +24,7 @@ namespace pdu
 {
 
 /// @brief Presentation Context item for a A-ASSOCIATE-RQ PDU (PS 3.8, 9.3.2.2).
-class PresentationContextRQ: public PresentationContext
+class ODIL_API PresentationContextRQ: public PresentationContext
 {
 public:
     /// @brief Constructor.
diff --git a/src/odil/pdu/RoleSelection.h b/src/odil/pdu/RoleSelection.h
index edcaaee..e828e71 100644
--- a/src/odil/pdu/RoleSelection.h
+++ b/src/odil/pdu/RoleSelection.h
@@ -13,6 +13,7 @@
 #include <istream>
 #include <string>
 
+#include "odil/odil.h"
 #include "odil/pdu/Object.h"
 
 namespace odil
@@ -22,7 +23,7 @@ namespace pdu
 {
 
 /// @brief SCU/SCP Role Selection Sub-Item (PS 3.7, D.3.3.4.1 and D.3.3.4.2).
-class RoleSelection: public Object
+class ODIL_API RoleSelection: public Object
 {
 public:
     /// @brief Item type.
diff --git a/src/odil/pdu/SOPClassCommonExtendedNegotiation.h b/src/odil/pdu/SOPClassCommonExtendedNegotiation.h
index 36ef78d..a0188e2 100644
--- a/src/odil/pdu/SOPClassCommonExtendedNegotiation.h
+++ b/src/odil/pdu/SOPClassCommonExtendedNegotiation.h
@@ -13,6 +13,7 @@
 #include <string>
 #include <vector>
 
+#include "odil/odil.h"
 #include "odil/pdu/Object.h"
 
 namespace odil
@@ -22,7 +23,7 @@ namespace pdu
 {
 
 /// @brief SOP Class Common Extended Negotiation sub-item (PS 3.7, D.3.3.6).
-class SOPClassCommonExtendedNegotiation: public Object
+class ODIL_API SOPClassCommonExtendedNegotiation: public Object
 {
 public:
     /// @brief Item type.
diff --git a/src/odil/pdu/SOPClassExtendedNegotiation.h b/src/odil/pdu/SOPClassExtendedNegotiation.h
index 91acf0f..638ed79 100644
--- a/src/odil/pdu/SOPClassExtendedNegotiation.h
+++ b/src/odil/pdu/SOPClassExtendedNegotiation.h
@@ -14,6 +14,7 @@
 #include <string>
 #include <vector>
 
+#include "odil/odil.h"
 #include "odil/pdu/Object.h"
 
 namespace odil
@@ -23,7 +24,7 @@ namespace pdu
 {
 
 /// @brief SOP Class Extended Negotiation sub-item (PS 3.7, D.3.3.5).
-class SOPClassExtendedNegotiation: public Object
+class ODIL_API SOPClassExtendedNegotiation: public Object
 {
 public:
     /// @brief Item type.
diff --git a/src/odil/pdu/UserIdentityAC.h b/src/odil/pdu/UserIdentityAC.h
index c646ed6..32d6b86 100644
--- a/src/odil/pdu/UserIdentityAC.h
+++ b/src/odil/pdu/UserIdentityAC.h
@@ -12,6 +12,7 @@
 #include <istream>
 #include <string>
 
+#include "odil/odil.h"
 #include "odil/pdu/Object.h"
 
 namespace odil
@@ -21,7 +22,7 @@ namespace pdu
 {
 
 /// @brief User Identity Sub-Item Structure (A-ASSOCIATE-AC) (PS 3.7, D.3.3.7.2).
-class UserIdentityAC: public Object
+class ODIL_API UserIdentityAC: public Object
 {
 public:
     /// @brief Item type.
diff --git a/src/odil/pdu/UserIdentityRQ.h b/src/odil/pdu/UserIdentityRQ.h
index 3289cfe..3d6d104 100644
--- a/src/odil/pdu/UserIdentityRQ.h
+++ b/src/odil/pdu/UserIdentityRQ.h
@@ -13,6 +13,7 @@
 #include <istream>
 #include <string>
 
+#include "odil/odil.h"
 #include "odil/pdu/Object.h"
 
 namespace odil
@@ -22,7 +23,7 @@ namespace pdu
 {
 
 /// @brief User Identity Sub-Item Structure (A-ASSOCIATE-RQ) (PS 3.7, D.3.3.7.1).
-class UserIdentityRQ: public Object
+class ODIL_API UserIdentityRQ: public Object
 {
 public:
     /// @brief Item type.
diff --git a/src/odil/pdu/UserInformation.h b/src/odil/pdu/UserInformation.h
index 3f7c6fb..7ba4caa 100644
--- a/src/odil/pdu/UserInformation.h
+++ b/src/odil/pdu/UserInformation.h
@@ -13,6 +13,7 @@
 #include <istream>
 #include <vector>
 
+#include "odil/odil.h"
 #include "odil/pdu/MaximumLength.h"
 #include "odil/pdu/Object.h"
 #include "odil/pdu/UserIdentityAC.h"
@@ -25,7 +26,7 @@ namespace pdu
 {
 
 /// @brief User Information Item Structure (PS 3.8, 9.3.2.3 and 9.3.3.3).
-class UserInformation: public Object
+class ODIL_API UserInformation: public Object
 {
 public:
     /// @brief Create a User Information item with no sub-items.
diff --git a/src/odil/registry.cpp b/src/odil/registry.cpp
index d15a09d..186615b 100644
--- a/src/odil/registry.cpp
+++ b/src/odil/registry.cpp
@@ -618,6 +618,14 @@ ElementsDictionary create_public_dictionary()
           "Strain Additional Information", "StrainAdditionalInformation",  "UT", "1" },
         { Tag(0x0010, 0x0219),
           "Strain Code Sequence", "StrainCodeSequence",  "SQ", "1" },
+        { Tag(0x0010, 0x0221),
+          "Genetic Modifications Sequence", "GeneticModificationsSequence",  "SQ", "1" },
+        { Tag(0x0010, 0x0222),
+          "Genetic Modifications Description", "GeneticModificationsDescription",  "UC", "1" },
+        { Tag(0x0010, 0x0223),
+          "Genetic Modifications Nomenclature", "GeneticModificationsNomenclature",  "LO", "1" },
+        { Tag(0x0010, 0x0229),
+          "Genetic Modifications Code Sequence", "GeneticModificationsCodeSequence",  "SQ", "1" },
         { Tag(0x0010, 0x1000),
           "Other Patient IDs", "OtherPatientIDs",  "LO", "1-n" },
         { Tag(0x0010, 0x1001),
@@ -3726,6 +3734,12 @@ ElementsDictionary create_public_dictionary()
           "Segmented Blue Palette Color Lookup Table Data", "SegmentedBluePaletteColorLookupTableData",  "OW", "1" },
         { Tag(0x0028, 0x1224),
           "Segmented Alpha Palette Color Lookup Table Data", "SegmentedAlphaPaletteColorLookupTableData",  "OW", "1" },
+        { Tag(0x0028, 0x1230),
+          "Stored Value Color Range Sequence", "StoredValueColorRangeSequence",  "SQ", "1" },
+        { Tag(0x0028, 0x1231),
+          "Minimum Stored Value Mapped", "MinimumStoredValueMapped",  "FD", "1" },
+        { Tag(0x0028, 0x1232),
+          "Maximum Stored Value Mapped", "MaximumStoredValueMapped",  "FD", "1" },
         { Tag(0x0028, 0x1300),
           "Breast Implant Present", "BreastImplantPresent",  "CS", "1" },
         { Tag(0x0028, 0x1350),
@@ -5324,6 +5338,8 @@ ElementsDictionary create_public_dictionary()
           "Segment Label", "SegmentLabel",  "LO", "1" },
         { Tag(0x0062, 0x0006),
           "Segment Description", "SegmentDescription",  "ST", "1" },
+        { Tag(0x0062, 0x0007),
+          "Segmentation Algorithm Identification Sequence", "SegmentationAlgorithmIdentificationSequence",  "SQ", "1" },
         { Tag(0x0062, 0x0008),
           "Segment Algorithm Type", "SegmentAlgorithmType",  "CS", "1" },
         { Tag(0x0062, 0x0009),
@@ -5537,7 +5553,7 @@ ElementsDictionary create_public_dictionary()
         { Tag(0x0068, 0x62e0),
           "View Orientation Code Sequence", "ViewOrientationCodeSequence",  "SQ", "1" },
         { Tag(0x0068, 0x62f0),
-          "View Orientation Modifier", "ViewOrientationModifier",  "FD", "9" },
+          "View Orientation Modifier Code Sequence", "ViewOrientationModifierCodeSequence",  "SQ", "1" },
         { Tag(0x0068, 0x62f2),
           "HPGL Document Scaling", "HPGLDocumentScaling",  "FD", "1" },
         { Tag(0x0068, 0x6300),
@@ -6359,7 +6375,7 @@ ElementsDictionary create_public_dictionary()
         { Tag(0x0076, 0x0032),
           "Component Types Sequence", "ComponentTypesSequence",  "SQ", "1" },
         { Tag(0x0076, 0x0034),
-          "Component Type Code Sequence", "ComponentTypeCodeSequence",  "CS", "1" },
+          "Component Type Code Sequence", "ComponentTypeCodeSequence",  "SQ", "1" },
         { Tag(0x0076, 0x0036),
           "Exclusive Component Type", "ExclusiveComponentType",  "CS", "1" },
         { Tag(0x0076, 0x0038),
@@ -7095,6 +7111,8 @@ ElementsDictionary create_public_dictionary()
           "Parameter Pointer", "ParameterPointer",  "AT", "1" },
         { Tag(0x3008, 0x0066),
           "Override Reason", "OverrideReason",  "ST", "1" },
+        { Tag(0x3008, 0x0067),
+          "Parameter Value Number", "ParameterValueNumber",  "US", "1" },
         { Tag(0x3008, 0x0068),
           "Corrected Parameter Sequence", "CorrectedParameterSequence",  "SQ", "1" },
         { Tag(0x3008, 0x006a),
@@ -7885,6 +7903,8 @@ ElementsDictionary create_public_dictionary()
           "Range Modulator Gating Stop Water Equivalent Thickness", "RangeModulatorGatingStopWaterEquivalentThickness",  "FL", "1" },
         { Tag(0x300a, 0x038a),
           "Isocenter to Range Modulator Distance", "IsocenterToRangeModulatorDistance",  "FL", "1" },
+        { Tag(0x300a, 0x038f),
+          "Scan Spot Time Offset", "ScanSpotTimeOffset",  "FL", "1-n" },
         { Tag(0x300a, 0x0390),
           "Scan Spot Tune ID", "ScanSpotTuneID",  "SH", "1" },
         { Tag(0x300a, 0x0391),
@@ -8869,7 +8889,11 @@ std::map<std::string, odil::Tag> create_public_tags()
           "Strain Stock Sequence", "StrainStockSequence",  "SQ", "1" },    { Tag(0x0010, 0x0217),
           "Strain Source", "StrainSource",  "LO", "1" },    { Tag(0x0010, 0x0218),
           "Strain Additional Information", "StrainAdditionalInformation",  "UT", "1" },    { Tag(0x0010, 0x0219),
-          "Strain Code Sequence", "StrainCodeSequence",  "SQ", "1" },    { Tag(0x0010, 0x1000),
+          "Strain Code Sequence", "StrainCodeSequence",  "SQ", "1" },    { Tag(0x0010, 0x0221),
+          "Genetic Modifications Sequence", "GeneticModificationsSequence",  "SQ", "1" },    { Tag(0x0010, 0x0222),
+          "Genetic Modifications Description", "GeneticModificationsDescription",  "UC", "1" },    { Tag(0x0010, 0x0223),
+          "Genetic Modifications Nomenclature", "GeneticModificationsNomenclature",  "LO", "1" },    { Tag(0x0010, 0x0229),
+          "Genetic Modifications Code Sequence", "GeneticModificationsCodeSequence",  "SQ", "1" },    { Tag(0x0010, 0x1000),
           "Other Patient IDs", "OtherPatientIDs",  "LO", "1-n" },    { Tag(0x0010, 0x1001),
           "Other Patient Names", "OtherPatientNames",  "PN", "1-n" },    { Tag(0x0010, 0x1002),
           "Other Patient IDs Sequence", "OtherPatientIDsSequence",  "SQ", "1" },    { Tag(0x0010, 0x1005),
@@ -10408,7 +10432,10 @@ std::map<std::string, odil::Tag> create_public_tags()
           "Segmented Red Palette Color Lookup Table Data", "SegmentedRedPaletteColorLookupTableData",  "OW", "1" },    { Tag(0x0028, 0x1222),
           "Segmented Green Palette Color Lookup Table Data", "SegmentedGreenPaletteColorLookupTableData",  "OW", "1" },    { Tag(0x0028, 0x1223),
           "Segmented Blue Palette Color Lookup Table Data", "SegmentedBluePaletteColorLookupTableData",  "OW", "1" },    { Tag(0x0028, 0x1224),
-          "Segmented Alpha Palette Color Lookup Table Data", "SegmentedAlphaPaletteColorLookupTableData",  "OW", "1" },    { Tag(0x0028, 0x1300),
+          "Segmented Alpha Palette Color Lookup Table Data", "SegmentedAlphaPaletteColorLookupTableData",  "OW", "1" },    { Tag(0x0028, 0x1230),
+          "Stored Value Color Range Sequence", "StoredValueColorRangeSequence",  "SQ", "1" },    { Tag(0x0028, 0x1231),
+          "Minimum Stored Value Mapped", "MinimumStoredValueMapped",  "FD", "1" },    { Tag(0x0028, 0x1232),
+          "Maximum Stored Value Mapped", "MaximumStoredValueMapped",  "FD", "1" },    { Tag(0x0028, 0x1300),
           "Breast Implant Present", "BreastImplantPresent",  "CS", "1" },    { Tag(0x0028, 0x1350),
           "Partial View", "PartialView",  "CS", "1" },    { Tag(0x0028, 0x1351),
           "Partial View Description", "PartialViewDescription",  "ST", "1" },    { Tag(0x0028, 0x1352),
@@ -11207,7 +11234,8 @@ std::map<std::string, odil::Tag> create_public_tags()
           "Segmented Property Category Code Sequence", "SegmentedPropertyCategoryCodeSequence",  "SQ", "1" },    { Tag(0x0062, 0x0004),
           "Segment Number", "SegmentNumber",  "US", "1" },    { Tag(0x0062, 0x0005),
           "Segment Label", "SegmentLabel",  "LO", "1" },    { Tag(0x0062, 0x0006),
-          "Segment Description", "SegmentDescription",  "ST", "1" },    { Tag(0x0062, 0x0008),
+          "Segment Description", "SegmentDescription",  "ST", "1" },    { Tag(0x0062, 0x0007),
+          "Segmentation Algorithm Identification Sequence", "SegmentationAlgorithmIdentificationSequence",  "SQ", "1" },    { Tag(0x0062, 0x0008),
           "Segment Algorithm Type", "SegmentAlgorithmType",  "CS", "1" },    { Tag(0x0062, 0x0009),
           "Segment Algorithm Name", "SegmentAlgorithmName",  "LO", "1" },    { Tag(0x0062, 0x000a),
           "Segment Identification Sequence", "SegmentIdentificationSequence",  "SQ", "1" },    { Tag(0x0062, 0x000b),
@@ -11314,7 +11342,7 @@ std::map<std::string, odil::Tag> create_public_tags()
           "HPGL Document ID", "HPGLDocumentID",  "US", "1" },    { Tag(0x0068, 0x62d5),
           "HPGL Document Label", "HPGLDocumentLabel",  "LO", "1" },    { Tag(0x0068, 0x62e0),
           "View Orientation Code Sequence", "ViewOrientationCodeSequence",  "SQ", "1" },    { Tag(0x0068, 0x62f0),
-          "View Orientation Modifier", "ViewOrientationModifier",  "FD", "9" },    { Tag(0x0068, 0x62f2),
+          "View Orientation Modifier Code Sequence", "ViewOrientationModifierCodeSequence",  "SQ", "1" },    { Tag(0x0068, 0x62f2),
           "HPGL Document Scaling", "HPGLDocumentScaling",  "FD", "1" },    { Tag(0x0068, 0x6300),
           "HPGL Document", "HPGLDocument",  "OB", "1" },    { Tag(0x0068, 0x6310),
           "HPGL Contour Pen Number", "HPGLContourPenNumber",  "US", "1" },    { Tag(0x0068, 0x6320),
@@ -11725,7 +11753,7 @@ std::map<std::string, odil::Tag> create_public_tags()
           "Procedure Type Code Sequence", "ProcedureTypeCodeSequence",  "SQ", "1" },    { Tag(0x0076, 0x0030),
           "Surgical Technique", "SurgicalTechnique",  "LO", "1" },    { Tag(0x0076, 0x0032),
           "Component Types Sequence", "ComponentTypesSequence",  "SQ", "1" },    { Tag(0x0076, 0x0034),
-          "Component Type Code Sequence", "ComponentTypeCodeSequence",  "CS", "1" },    { Tag(0x0076, 0x0036),
+          "Component Type Code Sequence", "ComponentTypeCodeSequence",  "SQ", "1" },    { Tag(0x0076, 0x0036),
           "Exclusive Component Type", "ExclusiveComponentType",  "CS", "1" },    { Tag(0x0076, 0x0038),
           "Mandatory Component Type", "MandatoryComponentType",  "CS", "1" },    { Tag(0x0076, 0x0040),
           "Component Sequence", "ComponentSequence",  "SQ", "1" },    { Tag(0x0076, 0x0055),
@@ -12082,7 +12110,8 @@ std::map<std::string, odil::Tag> create_public_tags()
           "Parameter Item Index", "ParameterItemIndex",  "IS", "1" },    { Tag(0x3008, 0x0064),
           "Measured Dose Reference Number", "MeasuredDoseReferenceNumber",  "IS", "1" },    { Tag(0x3008, 0x0065),
           "Parameter Pointer", "ParameterPointer",  "AT", "1" },    { Tag(0x3008, 0x0066),
-          "Override Reason", "OverrideReason",  "ST", "1" },    { Tag(0x3008, 0x0068),
+          "Override Reason", "OverrideReason",  "ST", "1" },    { Tag(0x3008, 0x0067),
+          "Parameter Value Number", "ParameterValueNumber",  "US", "1" },    { Tag(0x3008, 0x0068),
           "Corrected Parameter Sequence", "CorrectedParameterSequence",  "SQ", "1" },    { Tag(0x3008, 0x006a),
           "Correction Value", "CorrectionValue",  "FL", "1" },    { Tag(0x3008, 0x0070),
           "Calculated Dose Reference Sequence", "CalculatedDoseReferenceSequence",  "SQ", "1" },    { Tag(0x3008, 0x0072),
@@ -12477,7 +12506,8 @@ std::map<std::string, odil::Tag> create_public_tags()
           "Range Modulator Gating Stop Value", "RangeModulatorGatingStopValue",  "FL", "1" },    { Tag(0x300a, 0x0386),
           "Range Modulator Gating Start Water Equivalent Thickness", "RangeModulatorGatingStartWaterEquivalentThickness",  "FL", "1" },    { Tag(0x300a, 0x0388),
           "Range Modulator Gating Stop Water Equivalent Thickness", "RangeModulatorGatingStopWaterEquivalentThickness",  "FL", "1" },    { Tag(0x300a, 0x038a),
-          "Isocenter to Range Modulator Distance", "IsocenterToRangeModulatorDistance",  "FL", "1" },    { Tag(0x300a, 0x0390),
+          "Isocenter to Range Modulator Distance", "IsocenterToRangeModulatorDistance",  "FL", "1" },    { Tag(0x300a, 0x038f),
+          "Scan Spot Time Offset", "ScanSpotTimeOffset",  "FL", "1-n" },    { Tag(0x300a, 0x0390),
           "Scan Spot Tune ID", "ScanSpotTuneID",  "SH", "1" },    { Tag(0x300a, 0x0391),
           "Scan Spot Prescribed Indices", "ScanSpotPrescribedIndices",  "IS", "1-n" },    { Tag(0x300a, 0x0392),
           "Number of Scan Spot Positions", "NumberOfScanSpotPositions",  "IS", "1" },    { Tag(0x300a, 0x0393),
@@ -12763,13 +12793,15 @@ UIDsDictionary create_uids_dictionary()
         { "1.2.840.10008.1.2.4.93", "JPEG 2000 Part 2 Multi-component Image Compression", "JPEG2000Part2MulticomponentImageCompression",  "Transfer Syntax" },
         { "1.2.840.10008.1.2.4.94", "JPIP Referenced", "JPIPReferenced",  "Transfer Syntax" },
         { "1.2.840.10008.1.2.4.95", "JPIP Referenced Deflate", "JPIPReferencedDeflate",  "Transfer Syntax" },
-        { "1.2.840.10008.1.2.4.100", "MPEG2 Main Profile @ Main Level", "MPEG2MainProfileMainLevel",  "Transfer Syntax" },
-        { "1.2.840.10008.1.2.4.101", "MPEG2 Main Profile @ High Level", "MPEG2MainProfileHighLevel",  "Transfer Syntax" },
+        { "1.2.840.10008.1.2.4.100", "MPEG2 Main Profile / Main Level", "MPEG2MainProfileMainLevel",  "Transfer Syntax" },
+        { "1.2.840.10008.1.2.4.101", "MPEG2 Main Profile / High Level", "MPEG2MainProfileHighLevel",  "Transfer Syntax" },
         { "1.2.840.10008.1.2.4.102", "MPEG-4 AVC/H.264 High Profile / Level 4.1", "MPEG4AVCH264HighProfileLevel41",  "Transfer Syntax" },
         { "1.2.840.10008.1.2.4.103", "MPEG-4 AVC/H.264 BD-compatible High Profile / Level 4.1", "MPEG4AVCH264BDcompatibleHighProfileLevel41",  "Transfer Syntax" },
         { "1.2.840.10008.1.2.4.104", "MPEG-4 AVC/H.264 High Profile / Level 4.2 For 2D Video", "MPEG4AVCH264HighProfileLevel42For2DVideo",  "Transfer Syntax" },
         { "1.2.840.10008.1.2.4.105", "MPEG-4 AVC/H.264 High Profile / Level 4.2 For 3D Video", "MPEG4AVCH264HighProfileLevel42For3DVideo",  "Transfer Syntax" },
         { "1.2.840.10008.1.2.4.106", "MPEG-4 AVC/H.264 Stereo High Profile / Level 4.2", "MPEG4AVCH264StereoHighProfileLevel42",  "Transfer Syntax" },
+        { "1.2.840.10008.1.2.4.107", "HEVC/H.265 Main Profile / Level 5.1", "HEVCH265MainProfileLevel51",  "Transfer Syntax" },
+        { "1.2.840.10008.1.2.4.108", "HEVC/H.265 Main 10 Profile / Level 5.1", "HEVCH265Main10ProfileLevel51",  "Transfer Syntax" },
         { "1.2.840.10008.1.2.5", "RLE Lossless", "RLELossless",  "Transfer Syntax" },
         { "1.2.840.10008.1.2.6.1", "RFC 2557 MIME encapsulation", "RFC2557MIMEencapsulation",  "Transfer Syntax" },
         { "1.2.840.10008.1.2.6.2", "XML Encoding", "XMLEncoding",  "Transfer Syntax" },
@@ -12798,6 +12830,10 @@ UIDsDictionary create_uids_dictionary()
         { "1.2.840.10008.1.5.2", "PET Color Palette SOP Instance", "PETColorPaletteSOPInstance",  "Well-known SOP Instance" },
         { "1.2.840.10008.1.5.3", "Hot Metal Blue Color Palette SOP Instance", "HotMetalBlueColorPaletteSOPInstance",  "Well-known SOP Instance" },
         { "1.2.840.10008.1.5.4", "PET 20 Step Color Palette SOP Instance", "PET20StepColorPaletteSOPInstance",  "Well-known SOP Instance" },
+        { "1.2.840.10008.1.5.5", "Spring Color Palette SOP Instance", "SpringColorPaletteSOPInstance",  "Well-known SOP Instance" },
+        { "1.2.840.10008.1.5.6", "Summer Color Palette SOP Instance", "SummerColorPaletteSOPInstance",  "Well-known SOP Instance" },
+        { "1.2.840.10008.1.5.7", "Fall Color Palette SOP Instance", "FallColorPaletteSOPInstance",  "Well-known SOP Instance" },
+        { "1.2.840.10008.1.5.8", "Winter Color Palette SOP Instance", "WinterColorPaletteSOPInstance",  "Well-known SOP Instance" },
         { "1.2.840.10008.1.9", "Basic Study Content Notification SOP Class (Retired)", "BasicStudyContentNotificationSOPClass_Retired",  "SOP Class" },
         { "1.2.840.10008.1.20", "Papyrus 3 Implicit VR Little Endian (Retired)", "Papyrus3ImplicitVRLittleEndian_Retired",  "Transfer Syntax" },
         { "1.2.840.10008.1.20.1", "Storage Commitment Push Model SOP Class", "StorageCommitmentPushModelSOPClass",  "SOP Class" },
@@ -12975,6 +13011,7 @@ UIDsDictionary create_uids_dictionary()
         { "1.2.840.10008.5.1.4.1.1.88.69", "Colon CAD SR Storage", "ColonCADSRStorage",  "SOP Class" },
         { "1.2.840.10008.5.1.4.1.1.88.70", "Implantation Plan SR Storage", "ImplantationPlanSRStorage",  "SOP Class" },
         { "1.2.840.10008.5.1.4.1.1.88.71", "Acquisition Context SR Storage", "AcquisitionContextSRStorage",  "SOP Class" },
+        { "1.2.840.10008.5.1.4.1.1.88.72", "Simplified Adult Echo SR Storage", "SimplifiedAdultEchoSRStorage",  "SOP Class" },
         { "1.2.840.10008.5.1.4.1.1.90.1", "Content Assessment Results Storage", "ContentAssessmentResultsStorage",  "SOP Class" },
         { "1.2.840.10008.5.1.4.1.1.104.1", "Encapsulated PDF Storage", "EncapsulatedPDFStorage",  "SOP Class" },
         { "1.2.840.10008.5.1.4.1.1.104.2", "Encapsulated CDA Storage", "EncapsulatedCDAStorage",  "SOP Class" },
diff --git a/src/odil/registry.h b/src/odil/registry.h
index ff10bf3..6bae532 100644
--- a/src/odil/registry.h
+++ b/src/odil/registry.h
@@ -313,6 +313,10 @@ Tag const StrainStockSequence(0x0010, 0x0216);
 Tag const StrainSource(0x0010, 0x0217);
 Tag const StrainAdditionalInformation(0x0010, 0x0218);
 Tag const StrainCodeSequence(0x0010, 0x0219);
+Tag const GeneticModificationsSequence(0x0010, 0x0221);
+Tag const GeneticModificationsDescription(0x0010, 0x0222);
+Tag const GeneticModificationsNomenclature(0x0010, 0x0223);
+Tag const GeneticModificationsCodeSequence(0x0010, 0x0229);
 Tag const OtherPatientIDs(0x0010, 0x1000);
 Tag const OtherPatientNames(0x0010, 0x1001);
 Tag const OtherPatientIDsSequence(0x0010, 0x1002);
@@ -1862,6 +1866,9 @@ Tag const SegmentedRedPaletteColorLookupTableData(0x0028, 0x1221);
 Tag const SegmentedGreenPaletteColorLookupTableData(0x0028, 0x1222);
 Tag const SegmentedBluePaletteColorLookupTableData(0x0028, 0x1223);
 Tag const SegmentedAlphaPaletteColorLookupTableData(0x0028, 0x1224);
+Tag const StoredValueColorRangeSequence(0x0028, 0x1230);
+Tag const MinimumStoredValueMapped(0x0028, 0x1231);
+Tag const MaximumStoredValueMapped(0x0028, 0x1232);
 Tag const BreastImplantPresent(0x0028, 0x1300);
 Tag const PartialView(0x0028, 0x1350);
 Tag const PartialViewDescription(0x0028, 0x1351);
@@ -2661,6 +2668,7 @@ Tag const SegmentedPropertyCategoryCodeSequence(0x0062, 0x0003);
 Tag const SegmentNumber(0x0062, 0x0004);
 Tag const SegmentLabel(0x0062, 0x0005);
 Tag const SegmentDescription(0x0062, 0x0006);
+Tag const SegmentationAlgorithmIdentificationSequence(0x0062, 0x0007);
 Tag const SegmentAlgorithmType(0x0062, 0x0008);
 Tag const SegmentAlgorithmName(0x0062, 0x0009);
 Tag const SegmentIdentificationSequence(0x0062, 0x000a);
@@ -2767,7 +2775,7 @@ Tag const HPGLDocumentSequence(0x0068, 0x62c0);
 Tag const HPGLDocumentID(0x0068, 0x62d0);
 Tag const HPGLDocumentLabel(0x0068, 0x62d5);
 Tag const ViewOrientationCodeSequence(0x0068, 0x62e0);
-Tag const ViewOrientationModifier(0x0068, 0x62f0);
+Tag const ViewOrientationModifierCodeSequence(0x0068, 0x62f0);
 Tag const HPGLDocumentScaling(0x0068, 0x62f2);
 Tag const HPGLDocument(0x0068, 0x6300);
 Tag const HPGLContourPenNumber(0x0068, 0x6310);
@@ -3543,6 +3551,7 @@ Tag const ParameterItemIndex(0x3008, 0x0063);
 Tag const MeasuredDoseReferenceNumber(0x3008, 0x0064);
 Tag const ParameterPointer(0x3008, 0x0065);
 Tag const OverrideReason(0x3008, 0x0066);
+Tag const ParameterValueNumber(0x3008, 0x0067);
 Tag const CorrectedParameterSequence(0x3008, 0x0068);
 Tag const CorrectionValue(0x3008, 0x006a);
 Tag const CalculatedDoseReferenceSequence(0x3008, 0x0070);
@@ -3938,6 +3947,7 @@ Tag const RangeModulatorGatingStopValue(0x300a, 0x0384);
 Tag const RangeModulatorGatingStartWaterEquivalentThickness(0x300a, 0x0386);
 Tag const RangeModulatorGatingStopWaterEquivalentThickness(0x300a, 0x0388);
 Tag const IsocenterToRangeModulatorDistance(0x300a, 0x038a);
+Tag const ScanSpotTimeOffset(0x300a, 0x038f);
 Tag const ScanSpotTuneID(0x300a, 0x0390);
 Tag const ScanSpotPrescribedIndices(0x300a, 0x0391);
 Tag const NumberOfScanSpotPositions(0x300a, 0x0392);
@@ -4277,6 +4287,8 @@ std::string const MPEG4AVCH264BDcompatibleHighProfileLevel41("1.2.840.10008.1.2.
 std::string const MPEG4AVCH264HighProfileLevel42For2DVideo("1.2.840.10008.1.2.4.104");
 std::string const MPEG4AVCH264HighProfileLevel42For3DVideo("1.2.840.10008.1.2.4.105");
 std::string const MPEG4AVCH264StereoHighProfileLevel42("1.2.840.10008.1.2.4.106");
+std::string const HEVCH265MainProfileLevel51("1.2.840.10008.1.2.4.107");
+std::string const HEVCH265Main10ProfileLevel51("1.2.840.10008.1.2.4.108");
 std::string const RLELossless("1.2.840.10008.1.2.5");
 std::string const RFC2557MIMEencapsulation("1.2.840.10008.1.2.6.1");
 std::string const XMLEncoding("1.2.840.10008.1.2.6.2");
@@ -4305,6 +4317,10 @@ std::string const HotIronColorPaletteSOPInstance("1.2.840.10008.1.5.1");
 std::string const PETColorPaletteSOPInstance("1.2.840.10008.1.5.2");
 std::string const HotMetalBlueColorPaletteSOPInstance("1.2.840.10008.1.5.3");
 std::string const PET20StepColorPaletteSOPInstance("1.2.840.10008.1.5.4");
+std::string const SpringColorPaletteSOPInstance("1.2.840.10008.1.5.5");
+std::string const SummerColorPaletteSOPInstance("1.2.840.10008.1.5.6");
+std::string const FallColorPaletteSOPInstance("1.2.840.10008.1.5.7");
+std::string const WinterColorPaletteSOPInstance("1.2.840.10008.1.5.8");
 std::string const BasicStudyContentNotificationSOPClass_Retired("1.2.840.10008.1.9");
 std::string const Papyrus3ImplicitVRLittleEndian_Retired("1.2.840.10008.1.20");
 std::string const StorageCommitmentPushModelSOPClass("1.2.840.10008.1.20.1");
@@ -4482,6 +4498,7 @@ std::string const RadiopharmaceuticalRadiationDoseSRStorage("1.2.840.10008.5.1.4
 std::string const ColonCADSRStorage("1.2.840.10008.5.1.4.1.1.88.69");
 std::string const ImplantationPlanSRStorage("1.2.840.10008.5.1.4.1.1.88.70");
 std::string const AcquisitionContextSRStorage("1.2.840.10008.5.1.4.1.1.88.71");
+std::string const SimplifiedAdultEchoSRStorage("1.2.840.10008.5.1.4.1.1.88.72");
 std::string const ContentAssessmentResultsStorage("1.2.840.10008.5.1.4.1.1.90.1");
 std::string const EncapsulatedPDFStorage("1.2.840.10008.5.1.4.1.1.104.1");
 std::string const EncapsulatedCDAStorage("1.2.840.10008.5.1.4.1.1.104.2");
diff --git a/src/odil/uid.h b/src/odil/uid.h
index e60d25c..79a019d 100644
--- a/src/odil/uid.h
+++ b/src/odil/uid.h
@@ -26,7 +26,7 @@ extern ODIL_API std::string implementation_class_uid;
 extern ODIL_API std::string implementation_version_name;
 
 /// @brief Generate a UID under the UID prefix.
-std::string generate_uid();
+std::string ODIL_API generate_uid();
 
 }
 
diff --git a/src/odil/unicode.h b/src/odil/unicode.h
index 03a6f94..3d62dcd 100644
--- a/src/odil/unicode.h
+++ b/src/odil/unicode.h
@@ -10,18 +10,20 @@
 #define _4a178325_e3d6_4f6f_9a18_ba6a983ee396
 
 #include <string>
+
+#include "odil/odil.h"
 #include "odil/Value.h"
 
 namespace odil
 {
 
 /// @brief Convert a string to its UTF-8 representation
-std::string as_utf8(
+ODIL_API std::string as_utf8(
     std::string const & input, Value::Strings const & specific_character_set,
     bool is_pn=false);
 
 /// @brief Convert an UTF-8 string to a specific representation
-std::string as_specific_character_set(
+ODIL_API std::string as_specific_character_set(
     std::string const & input, Value::Strings const & specific_character_set,
     bool is_pn=false);
 
diff --git a/src/odil/write_ds.h b/src/odil/write_ds.h
index 87b5cac..fed335c 100644
--- a/src/odil/write_ds.h
+++ b/src/odil/write_ds.h
@@ -14,11 +14,13 @@
 #include <cstdlib>
 #include <cstring>
 
+#include "odil/odil.h"
+
 namespace odil
 {
 
 /// @brief Write a double as a DS to the buffer.
-void write_ds(double f, char * buffer, int size=16);
+ODIL_API void write_ds(double f, char * buffer, int size=16);
 
 }
 
diff --git a/src/odil/xml_converter.cpp b/src/odil/xml_converter.cpp
index 94a7944..846e68b 100644
--- a/src/odil/xml_converter.cpp
+++ b/src/odil/xml_converter.cpp
@@ -388,15 +388,10 @@ DataSet as_dataset(boost::property_tree::ptree const & xml)
         Tag const tag(it->second.get<std::string>("<xmlattr>.tag"));
         VR const vr = as_vr(it->second.get<std::string>("<xmlattr>.vr"));
 
-        Element element;
+        Element element(vr);
 
-        if(vr == VR::AE || vr == VR::AS || vr == VR::AT || vr == VR::CS ||
-           vr == VR::DA || vr == VR::DT || vr == VR::LO || vr == VR::LT ||
-           vr == VR::SH || vr == VR::ST || vr == VR::TM || vr == VR::UI ||
-           vr == VR::UT)
+        if(odil::is_string(vr) && vr != odil::VR::PN)
         {
-            element = Element(Value::Strings(), vr);
-
             auto values = parse_value<Value::Strings::value_type>(it->second,
                                                                   vr);
 
@@ -407,8 +402,6 @@ DataSet as_dataset(boost::property_tree::ptree const & xml)
         }
         else if(vr == VR::PN)
         {
-            element = Element(Value::Strings(), vr);
-
             std::map<int, Value::Strings::value_type> values;
             for(auto it_value = it->second.begin();
                 it_value != it->second.end(); ++it_value)
@@ -479,10 +472,8 @@ DataSet as_dataset(boost::property_tree::ptree const & xml)
                 element.as_string().push_back(it->second);
             }
         }
-        else if(vr == VR::DS || vr == VR::FD || vr == VR::FL)
+        else if(is_real(vr))
         {
-            element = Element(Value::Reals(), vr);
-
             auto values = parse_value<Value::Reals::value_type>(it->second, vr);
 
             for (auto it = values.begin(); it != values.end(); ++it)
@@ -490,11 +481,8 @@ DataSet as_dataset(boost::property_tree::ptree const & xml)
                 element.as_real().push_back(it->second);
             }
         }
-        else if(vr == VR::IS || vr == VR::SL || vr == VR::SS ||
-                vr == VR::UL || vr == VR::US)
+        else if(is_int(vr))
         {
-            element = Element(Value::Integers(), vr);
-
             auto values = parse_value<Value::Integers::value_type>(it->second,
                                                                    vr);
 
@@ -505,8 +493,6 @@ DataSet as_dataset(boost::property_tree::ptree const & xml)
         }
         else if(vr == VR::SQ)
         {
-            element = Element(Value::DataSets(), vr);
-
             std::map<int, Value::DataSets::value_type> values;
             for(auto it_value = it->second.begin();
                 it_value != it->second.end(); ++it_value)
@@ -547,8 +533,6 @@ DataSet as_dataset(boost::property_tree::ptree const & xml)
         }
         else if(is_binary(vr))
         {
-            element = Element(Value::Binary(), vr);
-
             bool find_inline_binary = false; // only one Tag InlineBinary
             for(auto it_value = it->second.begin();
                 it_value != it->second.end(); ++it_value)
diff --git a/src/odil/xml_converter.h b/src/odil/xml_converter.h
index 5b01ef2..4be72bb 100644
--- a/src/odil/xml_converter.h
+++ b/src/odil/xml_converter.h
@@ -12,15 +12,16 @@
 #include <boost/property_tree/ptree.hpp>
 
 #include "odil/DataSet.h"
+#include "odil/odil.h"
 
 namespace odil
 {
 
 /// @brief Convert a data set to its XML representation.
-boost::property_tree::ptree as_xml(DataSet const & data_set);
+ODIL_API boost::property_tree::ptree as_xml(DataSet const & data_set);
 
 /// @brief Create a data set from its XML representation.
-DataSet as_dataset(boost::property_tree::ptree const & xml);
+ODIL_API DataSet as_dataset(boost::property_tree::ptree const & xml);
 
 } // namespace odil
 
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 2afa0eb..3962239 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -23,12 +23,15 @@ endif()
 include_directories(
     ${CMAKE_SOURCE_DIR}/src ${Boost_INCLUDE_DIRS} ${DCMTK_INCLUDE_DIRS}
     ${JsonCpp_INCLUDE_DIRS})
+
 add_definitions(
     ${DCMTK_DEFINITIONS}
-    -D BOOST_ASIO_DYN_LINK
     -D ODIL_MAJOR_VERSION=${odil_MAJOR_VERSION}
-    -DBOOST_TEST_DYN_LINK
 )
+if(BUILD_SHARED_LIBS)
+    add_definitions(-D BOOST_ALL_DYN_LINK)
+endif()
+
 link_directories(${Boost_LIBRARY_DIRS} ${DCMTK_LIBRARY_DIRS})
 
 
diff --git a/tests/code/BasicDirectoryCreator.cpp b/tests/code/BasicDirectoryCreator.cpp
index ec75cf0..8a511b6 100644
--- a/tests/code/BasicDirectoryCreator.cpp
+++ b/tests/code/BasicDirectoryCreator.cpp
@@ -49,9 +49,6 @@ BOOST_AUTO_TEST_CASE(Constructor)
 
 BOOST_AUTO_TEST_CASE(BasicDirectory)
 {
-    std::stringstream stream1;
-    stream1 <<  "{";
-
     {
         odil::DataSet data_set;
         data_set.add("PatientID", {"DJ123"});
@@ -68,7 +65,8 @@ BOOST_AUTO_TEST_CASE(BasicDirectory)
         data_set.add("SOPInstanceUID", {"1.2.3.4.1.1"});
         data_set.add("SOPClassUID", {odil::registry::RawDataStorage});
 
-        std::ofstream stream("a.dcm");
+        std::ofstream stream(
+            "a.dcm", std::ofstream::out | std::ofstream::binary);
         odil::Writer::write_file(data_set, stream);
     }
 
@@ -88,7 +86,8 @@ BOOST_AUTO_TEST_CASE(BasicDirectory)
         data_set.add("SOPInstanceUID", {"1.2.3.4.1.2"});
         data_set.add("SOPClassUID", {odil::registry::RawDataStorage});
 
-        std::ofstream stream("b.dcm");
+        std::ofstream stream(
+            "b.dcm", std::ofstream::out | std::ofstream::binary);
         odil::Writer::write_file(data_set, stream);
     }
 
@@ -99,8 +98,11 @@ BOOST_AUTO_TEST_CASE(BasicDirectory)
         ".", {"a.dcm", "b.dcm"}, extra_records);
     creator();
 
-    boost::filesystem::ifstream stream(boost::filesystem::path(".")/"DICOMDIR");
+    boost::filesystem::ifstream stream(
+        boost::filesystem::path(".")/"DICOMDIR",
+        boost::filesystem::ifstream::in | boost::filesystem::ifstream::binary);
     auto const dicomdir_and_header = odil::Reader::read_file(stream);
+    stream.close();
 
     BOOST_REQUIRE(
         dicomdir_and_header.first.as_string("MediaStorageSOPClassUID") ==
@@ -156,4 +158,5 @@ BOOST_AUTO_TEST_CASE(BasicDirectory)
 
     boost::filesystem::remove("a.dcm");
     boost::filesystem::remove("b.dcm");
+    boost::filesystem::remove("DICOMDIR");
 }
diff --git a/tests/code/DataSet.cpp b/tests/code/DataSet.cpp
index 891071d..0f790b0 100644
--- a/tests/code/DataSet.cpp
+++ b/tests/code/DataSet.cpp
@@ -94,266 +94,248 @@ BOOST_AUTO_TEST_CASE(AddInvalidTag)
     BOOST_CHECK_THROW(dataset.add(tag), odil::Exception);
 }
 
-BOOST_AUTO_TEST_CASE(AddIntEmpty)
-{
-    odil::Tag const tag("Rows");
-
-    odil::DataSet dataset;
-    dataset.add(tag);
-
-    BOOST_CHECK(dataset.is_int(tag));
-    BOOST_CHECK(dataset.empty(tag));
-    BOOST_CHECK_EQUAL(dataset.size(tag), 0);
-
-    std::vector<int64_t> const & value = dataset.as_int(tag);
-    BOOST_CHECK(value.empty());
-}
+template<typename TContainer>
+void test_element_value(
+    odil::DataSet const & data_set, odil::Tag const & tag,
+    TContainer const & contents,
+    bool (odil::DataSet::*type_check)(odil::Tag const &) const,
+    TContainer const & (odil::DataSet::*getter)(odil::Tag const &) const,
+    typename TContainer::value_type const & (odil::DataSet::*getter_pos)(odil::Tag const &, unsigned int) const)
+{
+    BOOST_CHECK(data_set.has(tag));
+    BOOST_CHECK_EQUAL(data_set.empty(tag), contents.empty());
+    BOOST_CHECK_EQUAL(data_set.size(tag), contents.size());
+    BOOST_CHECK((data_set.*type_check)(tag));
+    BOOST_CHECK((data_set.*getter)(tag) == contents);
+
+    for(int i=0; i<contents.size(); ++i)
+    {
+        BOOST_CHECK((data_set.*getter_pos)(tag, i) == contents[i]);
+    }
+    BOOST_CHECK_THROW((data_set.*getter_pos)(tag, contents.size()), odil::Exception);
+
+    if(!data_set.is_int(tag))
+    {
+        BOOST_CHECK_THROW(data_set.as_int(tag), odil::Exception);
+    }
+    if(!data_set.is_real(tag))
+    {
+        BOOST_CHECK_THROW(data_set.as_real(tag), odil::Exception);
+    }
+    if(!data_set.is_string(tag))
+    {
+        BOOST_CHECK_THROW(data_set.as_string(tag), odil::Exception);
+    }
+    if(!data_set.is_data_set(tag))
+    {
+        BOOST_CHECK_THROW(data_set.as_data_set(tag), odil::Exception);
+    }
+    if(!data_set.is_binary(tag))
+    {
+        BOOST_CHECK_THROW(data_set.as_binary(tag), odil::Exception);
+    }
+}
+
+template<typename TContainer>
+void test_implicit_container(
+    odil::Tag const & tag, 
+    bool (odil::DataSet::*type_check)(odil::Tag const &) const,
+    TContainer const & (odil::DataSet::*getter)(odil::Tag const &) const,
+    typename TContainer::value_type const & (odil::DataSet::*getter_pos)(odil::Tag const &, unsigned int) const)
+{
+    odil::DataSet data_set;
+    data_set.add(tag);
+    test_element_value(data_set, tag, TContainer(), type_check, getter, getter_pos);
+}
+
+template<typename TContainer>
+void test_implicit_container(
+    odil::Tag const & tag, odil::VR const & vr,
+    bool (odil::DataSet::*type_check)(odil::Tag const &) const,
+    TContainer const & (odil::DataSet::*getter)(odil::Tag const &) const,
+    typename TContainer::value_type const & (odil::DataSet::*getter_pos)(odil::Tag const &, unsigned int) const)
+{
+    odil::DataSet data_set;
+    data_set.add(tag, vr);
+    test_element_value(data_set, tag, TContainer(), type_check, getter, getter_pos);
+}
+
+template<typename TContainer>
+void test_container(
+    odil::Tag const & tag, TContainer const & contents,
+    bool (odil::DataSet::*type_check)(odil::Tag const &) const,
+    TContainer const & (odil::DataSet::*getter)(odil::Tag const &) const,
+    typename TContainer::value_type const & (odil::DataSet::*getter_pos)(odil::Tag const &, unsigned int) const)
+{
+    odil::DataSet data_set;
+    data_set.add(tag, contents);
+    test_element_value(data_set, tag, contents, type_check, getter, getter_pos);
+
+    auto contents_copy(contents);
+    odil::DataSet other_data_set;
+    other_data_set.add(tag, std::move(contents_copy));
+    BOOST_CHECK(contents_copy.empty());
+}
+
+template<typename TContainer>
+void test_container(
+    odil::Tag const & tag, TContainer const & contents, odil::VR const & vr,
+    bool (odil::DataSet::*type_check)(odil::Tag const &) const,
+    TContainer const & (odil::DataSet::*getter)(odil::Tag const &) const,
+    typename TContainer::value_type const & (odil::DataSet::*getter_pos)(odil::Tag const &, unsigned int) const)
+{
+    odil::DataSet data_set;
+    data_set.add(tag, contents, vr);
+    test_element_value(data_set, tag, contents, type_check, getter, getter_pos);
+
+    auto contents_copy(contents);
+    odil::DataSet other_data_set;
+    other_data_set.add(tag, std::move(contents_copy));
+    BOOST_CHECK(contents_copy.empty());
+}
+
+template<typename TContainer>
+void test_initializer_list(
+    odil::Tag const & tag, 
+    std::initializer_list<typename TContainer::value_type> const & contents,
+    bool (odil::DataSet::*type_check)(odil::Tag const &) const, 
+    TContainer const & (odil::DataSet::*getter)(odil::Tag const &) const,
+    typename TContainer::value_type const & (odil::DataSet::*getter_pos)(odil::Tag const &, unsigned int) const)
+{
+    odil::DataSet data_set;
+    data_set.add(tag, contents);
+    test_element_value(data_set, tag, TContainer(contents), type_check, getter, getter_pos);
+}
 
-BOOST_AUTO_TEST_CASE(AddDoubleEmpty)
+template<typename TContainer>
+void test_initializer_list(
+    odil::Tag const & tag, 
+    std::initializer_list<typename TContainer::value_type> const & contents,
+    odil::VR const & vr,
+    bool (odil::DataSet::*type_check)(odil::Tag const &) const, 
+    TContainer const & (odil::DataSet::*getter)(odil::Tag const &) const,
+    typename TContainer::value_type const & (odil::DataSet::*getter_pos)(odil::Tag const &, unsigned int) const)
 {
-    odil::Tag const tag("SpacingBetweenSlices");
-
-    odil::DataSet dataset;
-    dataset.add(tag);
-
-    BOOST_CHECK(dataset.is_real(tag));
-    BOOST_CHECK(dataset.empty(tag));
-    BOOST_CHECK_EQUAL(dataset.size(tag), 0);
-
-    std::vector<double> const & value = dataset.as_real(tag);
-    BOOST_CHECK(value.empty());
+    odil::DataSet data_set;
+    data_set.add(tag, contents, vr);
+    test_element_value(data_set, tag, TContainer(contents), type_check, getter, getter_pos);
 }
 
-BOOST_AUTO_TEST_CASE(AddStringEmpty)
+template<typename TContainer>
+void test_modify(
+    odil::Tag const & tag, TContainer const & contents,
+    TContainer const & (odil::DataSet::*getter)(odil::Tag const &) const,
+    TContainer & (odil::DataSet::*setter)(odil::Tag const &))
 {
-    odil::Tag const tag("PatientID");
-
-    odil::DataSet dataset;
-    dataset.add(tag);
-
-    BOOST_CHECK(dataset.is_string(tag));
-    BOOST_CHECK(dataset.empty(tag));
-    BOOST_CHECK_EQUAL(dataset.size(tag), 0);
-
-    std::vector<std::string> const & value = dataset.as_string(tag);
-    BOOST_CHECK(value.empty());
+    odil::DataSet data_set;
+    data_set.add(tag, {contents[0]});
+    (data_set.*setter)(tag).push_back(contents[1]);
+    BOOST_CHECK((data_set.*getter)(tag) == contents);
 }
 
-BOOST_AUTO_TEST_CASE(AddDataSetEmpty)
+template<typename TContainer>
+void test_clear(
+    odil::Tag const & tag, TContainer const & contents,
+    bool (odil::DataSet::*type_check)(odil::Tag const &) const)
 {
-    odil::Tag const tag("ReferencedStudySequence");
-
-    odil::DataSet dataset;
-    dataset.add(tag);
-
-    BOOST_CHECK(dataset.is_data_set(tag));
-    BOOST_CHECK(dataset.empty(tag));
-    BOOST_CHECK_EQUAL(dataset.size(tag), 0);
-
-    odil::Value::DataSets const & value = dataset.as_data_set(tag);
-    BOOST_CHECK(value.empty());
+    odil::DataSet data_set;
+    data_set.add(tag, contents);
+    data_set.clear(tag);
+    BOOST_CHECK((data_set.*type_check)(tag));
+    BOOST_CHECK(data_set.empty(tag));
 }
 
-BOOST_AUTO_TEST_CASE(AddBinaryEmpty)
+template<typename TContainer>
+void test_element(
+    odil::Tag const & tag, 
+    std::initializer_list<typename TContainer::value_type> const & contents,
+    odil::VR const & vr, 
+    bool (odil::DataSet::*type_check)(odil::Tag const &) const,
+    TContainer const & (odil::DataSet::*getter)(odil::Tag const &) const,
+    typename TContainer::value_type const & (odil::DataSet::*getter_pos)(odil::Tag const &, unsigned int) const,
+    TContainer & (odil::DataSet::*setter)(odil::Tag const &))
 {
-    odil::Tag const tag("BadPixelImage");
+    test_implicit_container(tag, type_check, getter, getter_pos);
+    test_implicit_container(tag, vr, type_check, getter, getter_pos);
 
-    odil::DataSet dataset;
-    dataset.add(tag);
+    TContainer const container(contents);
 
-    BOOST_CHECK(dataset.is_binary(tag));
-    BOOST_CHECK(dataset.empty(tag));
-    BOOST_CHECK_EQUAL(dataset.size(tag), 0);
+    test_container(tag, container, type_check, getter, getter_pos);
+    test_container(tag, container, vr, type_check, getter, getter_pos);
 
-    auto const & value = dataset.as_binary(tag);
-    BOOST_CHECK(value.empty());
-}
+    test_initializer_list(tag, contents, type_check, getter, getter_pos);
+    test_initializer_list(tag, contents, vr, type_check, getter, getter_pos);
 
-BOOST_AUTO_TEST_CASE(AddInt)
-{
-    odil::Tag const tag("Rows");
+    test_modify(tag, container, getter, setter);
 
-    odil::DataSet dataset;
-    dataset.add(tag, odil::Value::Integers({123}));
-
-    BOOST_CHECK(dataset.is_int(tag));
-    BOOST_REQUIRE_EQUAL(dataset.size(tag), 1);
-    BOOST_REQUIRE(dataset.as_int(tag) == odil::Value::Integers({123}));
+    test_clear(tag, container, type_check);
 }
 
-BOOST_AUTO_TEST_CASE(AddDouble)
+BOOST_AUTO_TEST_CASE(Int)
 {
-    odil::Tag const tag("SpacingBetweenSlices");
-
-    odil::DataSet dataset;
-    dataset.add(tag, odil::Value::Reals({123.456}));
+    odil::DataSet data_set;
+    data_set.add(odil::registry::Rows, {1234, 5678});
+    data_set.is_int(odil::registry::Rows);
 
-    BOOST_CHECK(dataset.is_real(tag));
-    BOOST_REQUIRE_EQUAL(dataset.size(tag), 1);
-    BOOST_REQUIRE(dataset.as_real(tag) == odil::Value::Reals({123.456}));
+    test_element<odil::Value::Integers>(
+        odil::registry::Rows, {1234, 5678}, odil::VR::US,
+        &odil::DataSet::is_int, 
+        &odil::DataSet::as_int, &odil::DataSet::as_int, &odil::DataSet::as_int);
 }
 
-BOOST_AUTO_TEST_CASE(AddString)
+BOOST_AUTO_TEST_CASE(Real)
 {
-    odil::Tag const tag("PatientID");
-
-    odil::DataSet dataset;
-    dataset.add(tag, odil::Value::Strings({"DJ123"}));
-
-    BOOST_CHECK(dataset.is_string(tag));
-    BOOST_REQUIRE_EQUAL(dataset.size(tag), 1);
-    BOOST_REQUIRE(dataset.as_string(tag) == odil::Value::Strings({"DJ123"}));
-}
-
-BOOST_AUTO_TEST_CASE(AddDataSet)
-{
-    odil::Tag const tag("ReferencedStudySequence");
-    odil::DataSet item;
-    item.add(odil::registry::StudyInstanceUID, {"1.2.3"});
-
-    odil::DataSet dataset;
-    dataset.add(tag, odil::Value::DataSets({item}));
+    odil::DataSet data_set;
+    data_set.add(odil::registry::SpacingBetweenSlices, {12.34, 56.78});
+    data_set.is_int(odil::registry::SpacingBetweenSlices);
 
-    BOOST_CHECK(dataset.is_data_set(tag));
-    BOOST_REQUIRE_EQUAL(dataset.size(tag), 1);
-    BOOST_REQUIRE(dataset.as_data_set(tag, 0) == item);
+    test_element<odil::Value::Reals>(
+        odil::registry::SpacingBetweenSlices, {12.34, 56.78}, odil::VR::FL,
+        &odil::DataSet::is_real, 
+        &odil::DataSet::as_real, &odil::DataSet::as_real, &odil::DataSet::as_real);
 }
 
-BOOST_AUTO_TEST_CASE(AddIntInitializer1)
+BOOST_AUTO_TEST_CASE(String)
 {
-    odil::Tag const tag("Rows");
+    odil::DataSet data_set;
+    data_set.add(odil::registry::PatientID, {"foo", "bar"});
+    data_set.is_int(odil::registry::PatientID);
 
-    odil::DataSet dataset;
-    dataset.add(tag, {123});
-
-    BOOST_CHECK(dataset.is_int(tag));
-    BOOST_REQUIRE_EQUAL(dataset.size(tag), 1);
-    BOOST_REQUIRE(dataset.as_int(tag) == odil::Value::Integers({123}));
+    test_element<odil::Value::Strings>(
+        odil::registry::PatientID, {"foo", "bar"}, odil::VR::LT,
+        &odil::DataSet::is_string, 
+        &odil::DataSet::as_string, &odil::DataSet::as_string, &odil::DataSet::as_string);
 }
 
-BOOST_AUTO_TEST_CASE(AddIntInitializer2)
+BOOST_AUTO_TEST_CASE(DataSets)
 {
-    odil::Tag const tag("Rows");
+    odil::DataSet data_set_1;
+    data_set_1.add("PatientID", {"DJ1234"});
 
-    odil::DataSet dataset;
-    dataset.add(tag, {odil::Value::Integer(123)});
+    odil::DataSet data_set_2;
+    data_set_2.add("EchoTime", {100});
 
-    BOOST_CHECK(dataset.is_int(tag));
-    BOOST_REQUIRE_EQUAL(dataset.size(tag), 1);
-    BOOST_REQUIRE(dataset.as_int(tag) == odil::Value::Integers({123}));
-}
-
-
-BOOST_AUTO_TEST_CASE(AddDoubleInitializer)
-{
-    odil::Tag const tag("SpacingBetweenSlices");
+    odil::DataSet data_set;
+    data_set.add(
+        odil::registry::ReferencedStudySequence, {data_set_1, data_set_2});
+    data_set.is_int(odil::registry::ReferencedStudySequence);
 
-    odil::DataSet dataset;
-    dataset.add(tag, {123.456});
-
-    BOOST_CHECK(dataset.is_real(tag));
-    BOOST_REQUIRE_EQUAL(dataset.size(tag), 1);
-    BOOST_REQUIRE(dataset.as_real(tag) == odil::Value::Reals({123.456}));
+    test_element<odil::Value::DataSets>(
+        odil::registry::ReferencedStudySequence, {data_set_1, data_set_2}, odil::VR::SQ,
+        &odil::DataSet::is_data_set, 
+        &odil::DataSet::as_data_set, &odil::DataSet::as_data_set, &odil::DataSet::as_data_set);
 }
 
-BOOST_AUTO_TEST_CASE(AddStringInitializer)
+BOOST_AUTO_TEST_CASE(Binary)
 {
-    odil::Tag const tag("PatientID");
+    odil::DataSet data_set;
+    data_set.add(odil::registry::BadPixelImage, {{0x1, 0x2}, {0x3}});
+    data_set.is_int(odil::registry::BadPixelImage);
 
-    odil::DataSet dataset;
-    dataset.add(tag, {"DJ123"});
-
-    BOOST_CHECK(dataset.is_string(tag));
-    BOOST_REQUIRE_EQUAL(dataset.size(tag), 1);
-    BOOST_REQUIRE(dataset.as_string(tag) == odil::Value::Strings({"DJ123"}));
-}
-
-BOOST_AUTO_TEST_CASE(AddDataSetInitializer)
-{
-    odil::Tag const tag("ReferencedStudySequence");
-    odil::DataSet item;
-    item.add(odil::registry::StudyInstanceUID, {"1.2.3"});
-
-    odil::DataSet dataset;
-    dataset.add(tag, {item});
-
-    BOOST_CHECK(dataset.is_data_set(tag));
-    BOOST_REQUIRE_EQUAL(dataset.size(tag), 1);
-    BOOST_REQUIRE(dataset.as_data_set(tag, 0) == item);
-}
-
-BOOST_AUTO_TEST_CASE(AddBinary)
-{
-    odil::Tag const tag("BadPixelImage");
-
-    odil::DataSet dataset;
-    dataset.add(tag, odil::Value::Binary({{0x01, 0x02}}));
-
-    BOOST_CHECK(dataset.is_binary(tag));
-    BOOST_REQUIRE(
-        dataset.as_binary(tag) == odil::Value::Binary({{ 0x01, 0x02 }}));
-}
-
-BOOST_AUTO_TEST_CASE(ModifyInt)
-{
-    odil::Tag const tag("Rows");
-
-    odil::DataSet dataset;
-    dataset.add(tag);
-    dataset.as_int(tag).push_back(256);
-
-    BOOST_CHECK(!dataset.empty(tag));
-    BOOST_CHECK_EQUAL(dataset.size(tag), 1);
-    BOOST_CHECK_EQUAL(dataset.as_int(tag, 0), 256);
-}
-
-BOOST_AUTO_TEST_CASE(ModifyDouble)
-{
-    odil::Tag const tag("SpacingBetweenSlices");
-
-    odil::DataSet dataset;
-    dataset.add(tag);
-    dataset.as_real(tag).push_back(3.14);
-
-    BOOST_CHECK(!dataset.empty(tag));
-    BOOST_CHECK_EQUAL(dataset.size(tag), 1);
-    BOOST_CHECK_EQUAL(dataset.as_real(tag, 0), 3.14);
-}
-
-BOOST_AUTO_TEST_CASE(ModifyString)
-{
-    odil::Tag const tag("PatientID");
-
-    odil::DataSet dataset;
-    dataset.add(tag);
-    dataset.as_string(tag).push_back("FooBar");
-
-    BOOST_CHECK(!dataset.empty(tag));
-    BOOST_CHECK_EQUAL(dataset.size(tag), 1);
-    BOOST_CHECK_EQUAL(dataset.as_string(tag, 0), "FooBar");
-}
-
-BOOST_AUTO_TEST_CASE(ModifyDataSet)
-{
-    odil::Tag const tag("ReferencedStudySequence");
-
-    odil::DataSet dataset;
-    dataset.add(tag);
-
-    odil::DataSet item;
-    item.add("PatientID", {"DJ1234"});
-    dataset.as_data_set(tag).push_back(item);
-
-    BOOST_CHECK(!dataset.empty(tag));
-    BOOST_CHECK_EQUAL(dataset.size(tag), 1);
-
-    odil::Value::DataSets const & value = dataset.as_data_set(tag);
-    BOOST_CHECK_EQUAL(value.size(), 1);
-    BOOST_CHECK_EQUAL(value.size(), 1);
-    BOOST_CHECK_EQUAL(value[0].size(), 1);
-    BOOST_CHECK(value[0].has("PatientID"));
-    BOOST_CHECK(
-        value[0].as_string("PatientID") == odil::Value::Strings({"DJ1234"}));
+    test_element<odil::Value::Binary>(
+        odil::registry::BadPixelImage, {{0x1, 0x2}, {0x3}}, odil::VR::OB,
+        &odil::DataSet::is_binary,
+        &odil::DataSet::as_binary, &odil::DataSet::as_binary, &odil::DataSet::as_binary);
 }
 
 BOOST_AUTO_TEST_CASE(ElementAccessor)
@@ -375,7 +357,7 @@ BOOST_AUTO_TEST_CASE(GetVRMissing)
     BOOST_CHECK_THROW(dataset.get_vr(tag), odil::Exception);
 }
 
-BOOST_AUTO_TEST_CASE(TestEmptyMissing)
+BOOST_AUTO_TEST_CASE(EmptyMissing)
 {
     odil::Tag const tag("PatientID");
     odil::DataSet dataset;
@@ -389,6 +371,13 @@ BOOST_AUTO_TEST_CASE(SizeMissing)
     BOOST_CHECK_THROW(dataset.size(tag), odil::Exception);
 }
 
+BOOST_AUTO_TEST_CASE(ClearMissing)
+{
+    odil::Tag const tag("PatientID");
+    odil::DataSet dataset;
+    BOOST_CHECK_THROW(dataset.clear(tag), odil::Exception);
+}
+
 BOOST_AUTO_TEST_CASE(Remove)
 {
     odil::Tag const tag("PatientID");
@@ -441,3 +430,13 @@ BOOST_AUTO_TEST_CASE(Difference)
     BOOST_CHECK(! (dataset1 != dataset2));
     BOOST_CHECK(dataset1 != dataset3);
 }
+
+BOOST_AUTO_TEST_CASE(Clear)
+{
+    odil::DataSet data_set;
+    data_set.add("PatientID", {"DJ1234"});
+    data_set.clear("PatientID");
+    BOOST_CHECK(data_set.empty("PatientID"));
+    BOOST_CHECK_THROW(data_set.clear("PatietName"), odil::Exception);
+}
+
diff --git a/tests/code/Element.cpp b/tests/code/Element.cpp
index be392f7..a1bcdc2 100644
--- a/tests/code/Element.cpp
+++ b/tests/code/Element.cpp
@@ -1,172 +1,293 @@
 #define BOOST_TEST_MODULE Element
 #include <boost/test/unit_test.hpp>
 
+#include <string>
+#include <utility>
+
 #include "odil/DataSet.h"
 #include "odil/Element.h"
 #include "odil/Exception.h"
 
-BOOST_AUTO_TEST_CASE(Empty)
+template<typename TContainer>
+void test_value(
+    odil::Element const & element,
+    odil::VR const & vr, TContainer const & contents,
+    bool (odil::Element::*type_check)() const,
+    TContainer const & (odil::Element::*getter)() const)
 {
-    odil::Element element;
-    BOOST_CHECK(element.empty());
-    BOOST_CHECK_EQUAL(element.size(), 0);
+    BOOST_CHECK((element.*type_check)());
+    BOOST_CHECK_EQUAL(element.empty(), contents.empty());
+    BOOST_CHECK_EQUAL(element.size(), contents.size());
+    BOOST_CHECK((element.*getter)() == contents);
+    BOOST_CHECK(element.vr == vr);
+
+    if(!element.is_int())
+    {
+        BOOST_CHECK_THROW(element.as_int(), odil::Exception);
+    }
+    if(!element.is_real())
+    {
+        BOOST_CHECK_THROW(element.as_real(), odil::Exception);
+    }
+    if(!element.is_string())
+    {
+        BOOST_CHECK_THROW(element.as_string(), odil::Exception);
+    }
+    if(!element.is_data_set())
+    {
+        BOOST_CHECK_THROW(element.as_data_set(), odil::Exception);
+    }
+    if(!element.is_binary())
+    {
+        BOOST_CHECK_THROW(element.as_binary(), odil::Exception);
+    }
 }
 
-BOOST_AUTO_TEST_CASE(Int)
+template<typename TContainer>
+void test_implicit_container(
+    odil::VR const & vr,
+    bool (odil::Element::*type_check)() const,
+    TContainer const & (odil::Element::*getter)() const)
 {
-    odil::Element const element((odil::Value::Integers()));
-    BOOST_CHECK(element.is_int());
-
-    std::vector<int64_t> const & value = element.as_int();
-    BOOST_CHECK(value.empty());
+    odil::Element const element(vr);
+    test_value(element, vr, TContainer(), type_check, getter);
 }
 
-BOOST_AUTO_TEST_CASE(ModifyInt)
+template<typename TContainer>
+void test_container(
+    TContainer const & contents, odil::VR const & vr,
+    bool (odil::Element::*type_check)() const,
+    TContainer const & (odil::Element::*getter)() const)
 {
-    odil::Element element({0});
-    element.as_int().push_back(1);
-
-    BOOST_CHECK(!element.empty());
-    BOOST_CHECK_EQUAL(element.size(), 2);
+    odil::Element const element(contents, vr);
+    test_value(element, vr, contents, type_check, getter);
 
-    std::vector<int64_t> const & value = element.as_int();
-    BOOST_CHECK(value == odil::Value::Integers({0, 1}));
+    auto contents_copy(contents);
+    odil::Element const other_element(std::move(contents_copy));
+    BOOST_CHECK(contents_copy.empty());
 }
 
-BOOST_AUTO_TEST_CASE(ModifyInt2)
+template<typename TContainer>
+void test_initializer_list(
+    std::initializer_list<typename TContainer::value_type> const & contents,
+    bool (odil::Element::*type_check)() const,
+    TContainer const & (odil::Element::*getter)() const)
 {
-    odil::Element element({odil::Value::Integer(0)});
-    element.as_int().push_back(1);
-
-    BOOST_CHECK(!element.empty());
-    BOOST_CHECK_EQUAL(element.size(), 2);
-
-    std::vector<int64_t> const & value = element.as_int();
-    BOOST_CHECK(value == odil::Value::Integers({0, 1}));
+    odil::Element const element(contents);
+    test_value(element, element.vr, TContainer(contents), type_check, getter);
 }
 
-BOOST_AUTO_TEST_CASE(IntWrong)
+template<typename TContainer>
+void test_modify(
+    TContainer const & contents,
+    TContainer const & (odil::Element::*getter)() const,
+    TContainer & (odil::Element::*setter)())
 {
-    odil::Element element((odil::Value::Integers()));
-    BOOST_CHECK_THROW(element.as_real(), odil::Exception);
-    BOOST_CHECK_THROW(element.as_string(), odil::Exception);
+    odil::Element value(TContainer{{contents[0]}});
+    (value.*setter)().push_back(contents[1]);
+    BOOST_CHECK((value.*getter)() == contents);
 }
 
-BOOST_AUTO_TEST_CASE(Double)
+template<typename TContainer>
+void test_clear(
+    TContainer const & contents, 
+    bool (odil::Element::*type_check)() const)
 {
-    odil::Element const element((odil::Value::Reals()));
-    BOOST_CHECK(element.is_real());
-
-    std::vector<double> const & value = element.as_real();
-    BOOST_CHECK(value.empty());
+    odil::Element element(contents);
+    element.clear();
+    BOOST_CHECK((element.*type_check)());
+    BOOST_CHECK(element.empty());
 }
 
-BOOST_AUTO_TEST_CASE(ModifyDouble)
+template<typename TContainer>
+void test_equality(
+    TContainer const & contents_1, TContainer const & contents_2,
+    odil::VR const & vr_1, odil::VR const & vr_2)
 {
-    odil::Element element({0.});
-    element.as_real().push_back(1.5);
-
-    BOOST_CHECK(!element.empty());
-    BOOST_CHECK_EQUAL(element.size(), 2);
-
-    std::vector<double> const & value = element.as_real();
-    BOOST_CHECK(value == odil::Value::Reals({0., 1.5}));
+    odil::Element const value_1(contents_1, vr_1);
+    odil::Element const value_2(contents_1, vr_1);
+    odil::Element const value_3(contents_1, vr_2);
+    odil::Element const value_4(contents_2, vr_1);
+
+    BOOST_CHECK(value_1 == value_2);
+    BOOST_CHECK( ! (value_1 == value_3));
+    BOOST_CHECK( ! (value_1 == value_4));
+
+    BOOST_CHECK(! (value_1 != value_2));
+    BOOST_CHECK(value_1 != value_3);
+    BOOST_CHECK(value_1 != value_4);
 }
 
-BOOST_AUTO_TEST_CASE(DoubleWrong)
+struct Visitor
+{
+    typedef std::pair<std::string, std::string> result_type;
+
+    result_type operator()(odil::VR const & vr) const
+    {
+        return std::make_pair(odil::as_string(vr), std::string());
+    }
+
+    template<typename T>
+    result_type operator()(odil::VR const & vr, T const & container) const
+    {
+        return std::make_pair(odil::as_string(vr), typeid(container).name());
+    }
+};
+
+template<typename TContainer>
+void test_visitor(
+    TContainer const & contents, odil::VR const & vr)
 {
-    odil::Element element((odil::Value::Reals()));
-    BOOST_CHECK_THROW(element.as_int(), odil::Exception);
-    BOOST_CHECK_THROW(element.as_string(), odil::Exception);
-    BOOST_CHECK_THROW(element.as_data_set(), odil::Exception);
+    odil::Element const element(contents, vr);
+    BOOST_REQUIRE(
+        odil::apply_visitor(Visitor(), element)
+        == std::make_pair(
+                odil::as_string(vr), 
+                contents.empty()?std::string():typeid(TContainer).name()));
 }
 
-BOOST_AUTO_TEST_CASE(String)
+
+template<typename TContainer>
+void test(
+   std::initializer_list<typename TContainer::value_type> const & contents,
+   std::initializer_list<typename TContainer::value_type> const & other_contents,
+   odil::VR const & vr, odil::VR const & other_vr,
+   bool (odil::Element::*type_check)() const,
+   TContainer const & (odil::Element::*getter)() const,
+   TContainer & (odil::Element::*setter)())
 {
-    odil::Element const element((odil::Value::Strings()));
-    BOOST_CHECK(element.is_string());
+    TContainer const container(contents);
+    TContainer const other_container(other_contents);
 
-    std::vector<std::string> const & value = element.as_string();
-    BOOST_CHECK(value.empty());
-}
+    test_container(TContainer(), vr, type_check, getter);
+    test_container(container, vr, type_check, getter);
+    test_initializer_list(contents, type_check, getter); 
+    
+    test_modify(container, getter, setter);
 
-BOOST_AUTO_TEST_CASE(ModifyString)
-{
-    odil::Element element({""});
-    element.as_string().push_back("foo");
+    test_clear(container, type_check);
 
-    BOOST_CHECK(!element.empty());
-    BOOST_CHECK_EQUAL(element.size(), 2);
+    test_equality(container, other_container, vr, other_vr);
 
-    std::vector<std::string> const & value = element.as_string();
-    BOOST_CHECK(value == odil::Value::Strings({"", "foo"}));
+    test_visitor(TContainer(), vr);
+    test_visitor(container, vr);
 }
 
-BOOST_AUTO_TEST_CASE(StringWrong)
+BOOST_AUTO_TEST_CASE(ImplicitType)
 {
-    odil::Element element((odil::Value::Strings()));
-    BOOST_CHECK_THROW(element.as_int(), odil::Exception);
-    BOOST_CHECK_THROW(element.as_real(), odil::Exception);
-    BOOST_CHECK_THROW(element.as_data_set(), odil::Exception);
+    test_implicit_container<odil::Value::Strings>(
+        odil::VR::AE, &odil::Element::is_string, &odil::Element::as_string);
+    test_implicit_container<odil::Value::Strings>(
+        odil::VR::AS, &odil::Element::is_string, &odil::Element::as_string);
+    test_implicit_container<odil::Value::Strings>(
+        odil::VR::AT, &odil::Element::is_string, &odil::Element::as_string);
+    test_implicit_container<odil::Value::Strings>(
+        odil::VR::CS, &odil::Element::is_string, &odil::Element::as_string);
+    test_implicit_container<odil::Value::Strings>(
+        odil::VR::DA, &odil::Element::is_string, &odil::Element::as_string);
+    test_implicit_container<odil::Value::Reals>(
+        odil::VR::DS, &odil::Element::is_real, &odil::Element::as_real);
+    test_implicit_container<odil::Value::Strings>(
+        odil::VR::DT, &odil::Element::is_string, &odil::Element::as_string);
+    test_implicit_container<odil::Value::Reals>(
+        odil::VR::FL, &odil::Element::is_real, &odil::Element::as_real);
+    test_implicit_container<odil::Value::Reals>(
+        odil::VR::FD, &odil::Element::is_real, &odil::Element::as_real);
+    test_implicit_container<odil::Value::Integers>(
+        odil::VR::IS, &odil::Element::is_int, &odil::Element::as_int);
+    test_implicit_container<odil::Value::Strings>(
+        odil::VR::LO, &odil::Element::is_string, &odil::Element::as_string);
+    test_implicit_container<odil::Value::Strings>(
+        odil::VR::LT, &odil::Element::is_string, &odil::Element::as_string);
+    test_implicit_container<odil::Value::Binary>(
+        odil::VR::OB, &odil::Element::is_binary, &odil::Element::as_binary);
+    test_implicit_container<odil::Value::Binary>(
+        odil::VR::OD, &odil::Element::is_binary, &odil::Element::as_binary);
+    test_implicit_container<odil::Value::Binary>(
+        odil::VR::OF, &odil::Element::is_binary, &odil::Element::as_binary);
+    test_implicit_container<odil::Value::Binary>(
+        odil::VR::OL, &odil::Element::is_binary, &odil::Element::as_binary);
+    test_implicit_container<odil::Value::Binary>(
+        odil::VR::OW, &odil::Element::is_binary, &odil::Element::as_binary);
+    test_implicit_container<odil::Value::Strings>(
+        odil::VR::PN, &odil::Element::is_string, &odil::Element::as_string);
+    test_implicit_container<odil::Value::Strings>(
+        odil::VR::SH, &odil::Element::is_string, &odil::Element::as_string);
+    test_implicit_container<odil::Value::Integers>(
+        odil::VR::SL, &odil::Element::is_int, &odil::Element::as_int);
+    test_implicit_container<odil::Value::DataSets>(
+        odil::VR::SQ, &odil::Element::is_data_set, &odil::Element::as_data_set);
+    test_implicit_container<odil::Value::Integers>(
+        odil::VR::SS, &odil::Element::is_int, &odil::Element::as_int);
+    test_implicit_container<odil::Value::Strings>(
+        odil::VR::ST, &odil::Element::is_string, &odil::Element::as_string);
+    test_implicit_container<odil::Value::Strings>(
+        odil::VR::TM, &odil::Element::is_string, &odil::Element::as_string);
+    test_implicit_container<odil::Value::Strings>(
+        odil::VR::UC, &odil::Element::is_string, &odil::Element::as_string);
+    test_implicit_container<odil::Value::Strings>(
+        odil::VR::UI, &odil::Element::is_string, &odil::Element::as_string);
+    test_implicit_container<odil::Value::Integers>(
+        odil::VR::UL, &odil::Element::is_int, &odil::Element::as_int);
+    test_implicit_container<odil::Value::Binary>(
+        odil::VR::UN, &odil::Element::is_binary, &odil::Element::as_binary);
+    test_implicit_container<odil::Value::Strings>(
+        odil::VR::UR, &odil::Element::is_string, &odil::Element::as_string);
+    test_implicit_container<odil::Value::Integers>(
+        odil::VR::US, &odil::Element::is_int, &odil::Element::as_int);
+    test_implicit_container<odil::Value::Strings>(
+        odil::VR::UT, &odil::Element::is_string, &odil::Element::as_string);
 }
 
-BOOST_AUTO_TEST_CASE(DataSet)
+BOOST_AUTO_TEST_CASE(Int)
 {
-    odil::Element const element((odil::Value::DataSets()));
-    BOOST_CHECK(element.is_data_set());
-
-    odil::Value::DataSets const & value = element.as_data_set();
-    BOOST_CHECK(value.empty());
+    BOOST_CHECK(odil::Element({1234, 5678}).is_int());
+    test<odil::Value::Integers>(
+        {1234, 5678}, {9012, 3456}, odil::VR::US, odil::VR::UL, 
+        &odil::Element::is_int,
+        &odil::Element::as_int, &odil::Element::as_int);
 }
 
-BOOST_AUTO_TEST_CASE(ModifyDataSet)
+BOOST_AUTO_TEST_CASE(Real)
 {
-    odil::Element element((odil::Value::DataSets()));
-
-    odil::DataSet data_set;
-    data_set.add("PatientID");
-    data_set.as_string("PatientID").push_back("DJ1234");
-    element.as_data_set().push_back(data_set);
-
-    BOOST_CHECK(!element.empty());
-    BOOST_CHECK_EQUAL(element.size(), 1);
-
-    odil::Value::DataSets const & value = element.as_data_set();
-    BOOST_CHECK_EQUAL(value.size(), 1);
-    BOOST_CHECK_EQUAL(value[0].size(), 1);
-    BOOST_CHECK(value[0].has("PatientID"));
-    BOOST_CHECK(
-        value[0].as_string("PatientID") == odil::Value::Strings({"DJ1234"}));
+    BOOST_CHECK(odil::Element({12.34, 56.78}).is_real());
+    test<odil::Value::Reals>(
+        {12.34, 56.78}, {1., 2.}, odil::VR::FD, odil::VR::DS, 
+        &odil::Element::is_real,
+        &odil::Element::as_real, &odil::Element::as_real);
 }
 
-BOOST_AUTO_TEST_CASE(DataSetWrong)
+BOOST_AUTO_TEST_CASE(String)
 {
-    odil::Element element((odil::Value::DataSets()));
-    BOOST_CHECK_THROW(element.as_int(), odil::Exception);
-    BOOST_CHECK_THROW(element.as_real(), odil::Exception);
-    BOOST_CHECK_THROW(element.as_string(), odil::Exception);
+    BOOST_CHECK(odil::Element({"foo", "bar"}).is_string());
+    test<odil::Value::Strings>(
+        {"foo", "bar"}, {"plip", "plop"}, odil::VR::CS, odil::VR::UT, 
+        &odil::Element::is_string,
+        &odil::Element::as_string, &odil::Element::as_string);
 }
 
-BOOST_AUTO_TEST_CASE(Equality)
+BOOST_AUTO_TEST_CASE(DataSets)
 {
-    odil::Element const element1({12,34}, odil::VR::US);
-    odil::Element const element2({12,34}, odil::VR::US);
-    odil::Element const element3({12.,34.}, odil::VR::FL);
-    odil::Element const element4({12,34}, odil::VR::UL);
-
-    BOOST_CHECK(element1 == element2);
-    BOOST_CHECK(! (element1 == element3));
-    BOOST_CHECK(! (element1 == element4));
+    odil::DataSet data_set_1;
+    data_set_1.add("PatientID", {"DJ1234"});
+
+    odil::DataSet data_set_2;
+    data_set_2.add("EchoTime", {100});
+
+    BOOST_CHECK(odil::Element({data_set_1, data_set_2}).is_data_set());
+    test<odil::Value::DataSets>(
+        {data_set_1, data_set_2}, {data_set_2, data_set_1}, 
+        odil::VR::SQ, odil::VR::UN,
+        &odil::Element::is_data_set,
+        &odil::Element::as_data_set, &odil::Element::as_data_set);
 }
 
-BOOST_AUTO_TEST_CASE(Difference)
+BOOST_AUTO_TEST_CASE(Binary)
 {
-    odil::Element const element1({12,34}, odil::VR::US);
-    odil::Element const element2({12,34}, odil::VR::US);
-    odil::Element const element3({12.,34.}, odil::VR::FL);
-    odil::Element const element4({12,34}, odil::VR::UL);
-
-    BOOST_CHECK(! (element1 != element2));
-    BOOST_CHECK(element1 != element3);
-    BOOST_CHECK(element1 != element4);
+    BOOST_CHECK(odil::Element({{0x1, 0x2}, {0x3}}).is_binary());
+    test<odil::Value::Binary>(
+        {{0x1, 0x2}, {0x3}}, {{0x4}, {0x5, 0x6}},
+        odil::VR::OB, odil::VR::OW,
+        &odil::Element::is_binary,
+        &odil::Element::as_binary, &odil::Element::as_binary);
 }
diff --git a/tests/code/FindSCP.cpp b/tests/code/FindSCP.cpp
index fdc4f6a..a0af937 100644
--- a/tests/code/FindSCP.cpp
+++ b/tests/code/FindSCP.cpp
@@ -43,12 +43,12 @@ public:
         odil::DataSet data_set_1;
         data_set_1.add(odil::registry::PatientName, {"Hello^World"});
         data_set_1.add(odil::registry::PatientID, {"1234"});
-        this->_responses.push_back(data_set_1);
+        this->_responses.push_back(std::move(data_set_1));
 
         odil::DataSet data_set_2;
         data_set_2.add(odil::registry::PatientName, {"Doe^John"});
         data_set_2.add(odil::registry::PatientID, {"5678"});
-        this->_responses.push_back(data_set_2);
+        this->_responses.push_back(std::move(data_set_2));
 
         this->_response_iterator = this->_responses.begin();
     }
@@ -60,7 +60,7 @@ public:
 
     virtual odil::DataSet get() const
     {
-        return *this->_response_iterator;
+        return *std::make_move_iterator(this->_response_iterator);
     }
 
     virtual void next()
@@ -70,8 +70,8 @@ public:
 
 
 private:
-    std::vector<odil::DataSet> _responses;
-    std::vector<odil::DataSet>::const_iterator _response_iterator;
+    mutable std::vector<odil::DataSet> _responses;
+    std::vector<odil::DataSet>::iterator _response_iterator;
 };
 
 void run_server(Status * status)
@@ -88,8 +88,8 @@ void run_server(Status * status)
         find_scp.set_generator(generator);
 
         // Get echo message
-        auto const message = association.receive_message();
-        find_scp(message);
+        auto message = association.receive_message();
+        find_scp(std::move(message));
         // Should throw with peer closing connection
         association.receive_message();
     }
@@ -134,8 +134,8 @@ void run_client(Status * status)
         }
 
         std::ifstream stream(it->path().string());
-        auto const data_set = odil::Reader::read_file(stream).second;
-        status->responses.push_back(data_set);
+        auto data_set = odil::Reader::read_file(stream).second;
+        status->responses.push_back(std::move(data_set));
 
         boost::filesystem::remove(it->path());
     }
diff --git a/tests/code/FindSCU.cpp b/tests/code/FindSCU.cpp
index 33d4fa7..82607a4 100644
--- a/tests/code/FindSCU.cpp
+++ b/tests/code/FindSCU.cpp
@@ -43,6 +43,21 @@ BOOST_FIXTURE_TEST_CASE(Find, Fixture)
 
     scu.set_affected_sop_class(odil::registry::PatientRootQueryRetrieveInformationModelFIND);
     auto const results = scu.find(this->query);
+    BOOST_REQUIRE(!this->query.empty());
+
+    BOOST_REQUIRE_EQUAL(results.size(), 1);
+    BOOST_CHECK(
+        results[0].as_string("PatientID") ==
+            odil::Value::Strings({"DJ001"}));
+}
+
+BOOST_FIXTURE_TEST_CASE(FindMove, Fixture)
+{
+    odil::FindSCU scu(this->association);
+
+    scu.set_affected_sop_class(odil::registry::PatientRootQueryRetrieveInformationModelFIND);
+    auto const results = scu.find(std::move(this->query));
+    BOOST_REQUIRE(this->query.empty());
 
     BOOST_REQUIRE_EQUAL(results.size(), 1);
     BOOST_CHECK(
@@ -56,6 +71,18 @@ BOOST_FIXTURE_TEST_CASE(FindCallback, Fixture)
 
     scu.set_affected_sop_class(odil::registry::PatientRootQueryRetrieveInformationModelFIND);
     scu.find(this->query, Fixture::callback);
+    BOOST_REQUIRE(!this->query.empty());
+
+    BOOST_CHECK(Fixture::called);
+}
+
+BOOST_FIXTURE_TEST_CASE(FindCallbackMove, Fixture)
+{
+    odil::FindSCU scu(this->association);
+
+    scu.set_affected_sop_class(odil::registry::PatientRootQueryRetrieveInformationModelFIND);
+    scu.find(std::move(this->query), Fixture::callback);
+    BOOST_REQUIRE(this->query.empty());
 
     BOOST_CHECK(Fixture::called);
 }
diff --git a/tests/code/GetSCP.cpp b/tests/code/GetSCP.cpp
index 1c0a9e3..e0468dd 100644
--- a/tests/code/GetSCP.cpp
+++ b/tests/code/GetSCP.cpp
@@ -49,7 +49,7 @@ public:
             {"1.2.826.0.1.3680043.9.5560.3127449359877365688774406533090568532"});
         data_set_1.add("PatientName", {"Hello^World"});
         data_set_1.add("PatientID", {"1234"});
-        this->_responses.push_back(data_set_1);
+        this->_responses.push_back(std::move(data_set_1));
 
         odil::DataSet data_set_2;
         data_set_2.add("SOPClassUID", {odil::registry::RawDataStorage});
@@ -58,7 +58,7 @@ public:
             {"1.2.826.0.1.3680043.9.5560.3221615743193123463515381981101110692"});
         data_set_2.add("PatientName", {"Doe^John"});
         data_set_2.add("PatientID", {"5678"});
-        this->_responses.push_back(data_set_2);
+        this->_responses.push_back(std::move(data_set_2));
 
         this->_response_iterator = this->_responses.begin();
     }
@@ -70,7 +70,7 @@ public:
 
     virtual odil::DataSet get() const
     {
-        return *this->_response_iterator;
+        return *std::make_move_iterator(this->_response_iterator);
     }
 
     virtual void next()
@@ -84,8 +84,8 @@ public:
     }
 
 private:
-    std::vector<odil::DataSet> _responses;
-    std::vector<odil::DataSet>::const_iterator _response_iterator;
+    mutable std::vector<odil::DataSet> _responses;
+    std::vector<odil::DataSet>::iterator _response_iterator;
 };
 
 void run_server(Status * status)
@@ -101,9 +101,9 @@ void run_server(Status * status)
         auto const generator = std::make_shared<Generator>();
         get_scp.set_generator(generator);
 
-        // Get echo message
-        auto const message = association.receive_message();
-        get_scp(message);
+        // Get C-GET message
+        auto message = association.receive_message();
+        get_scp(std::move(message));
         // Should throw with peer closing connection
         association.receive_message();
     }
@@ -149,8 +149,8 @@ void run_client(Status * status)
         }
 
         std::ifstream stream(it->path().string());
-        auto const data_set = odil::Reader::read_file(stream).second;
-        status->responses.push_back(data_set);
+        auto data_set = odil::Reader::read_file(stream).second;
+        status->responses.push_back(std::move(data_set));
 
         boost::filesystem::remove(it->path());
     }
diff --git a/tests/code/GetSCU.cpp b/tests/code/GetSCU.cpp
index 97f47fd..403e4ff 100644
--- a/tests/code/GetSCU.cpp
+++ b/tests/code/GetSCU.cpp
@@ -63,6 +63,21 @@ BOOST_FIXTURE_TEST_CASE(Get, Fixture)
                 {"2.25.95090344942250266709587559073467305647"}));
 }
 
+BOOST_FIXTURE_TEST_CASE(GetMove, Fixture)
+{
+    odil::GetSCU scu(this->association);
+
+    scu.set_affected_sop_class(
+        odil::registry::PatientRootQueryRetrieveInformationModelGET);
+    auto const results = scu.get(std::move(this->query));
+
+    BOOST_REQUIRE_EQUAL(results.size(), 1);
+    BOOST_CHECK(
+        results[0].as_string("SOPInstanceUID") ==
+            odil::Value::Strings(
+                {"2.25.95090344942250266709587559073467305647"}));
+}
+
 BOOST_FIXTURE_TEST_CASE(GetBothCallbacks, Fixture)
 {
     odil::GetSCU scu(this->association);
@@ -75,6 +90,19 @@ BOOST_FIXTURE_TEST_CASE(GetBothCallbacks, Fixture)
     BOOST_CHECK(Fixture::get_callback_called);
 }
 
+BOOST_FIXTURE_TEST_CASE(GetBothCallbacksMove, Fixture)
+{
+    odil::GetSCU scu(this->association);
+
+    scu.set_affected_sop_class(
+        odil::registry::PatientRootQueryRetrieveInformationModelGET);
+    scu.get(
+        std::move(this->query), Fixture::store_callback, Fixture::get_callback);
+
+    BOOST_CHECK(Fixture::store_callback_called);
+    BOOST_CHECK(Fixture::get_callback_called);
+}
+
 BOOST_FIXTURE_TEST_CASE(GetOnlyStoreCallback, Fixture)
 {
     odil::GetSCU scu(this->association);
@@ -86,3 +114,15 @@ BOOST_FIXTURE_TEST_CASE(GetOnlyStoreCallback, Fixture)
     BOOST_CHECK(Fixture::store_callback_called);
     BOOST_CHECK(!Fixture::get_callback_called);
 }
+
+BOOST_FIXTURE_TEST_CASE(GetOnlyStoreCallbackMove, Fixture)
+{
+    odil::GetSCU scu(this->association);
+
+    scu.set_affected_sop_class(
+        odil::registry::PatientRootQueryRetrieveInformationModelGET);
+    scu.get(std::move(this->query), Fixture::store_callback);
+
+    BOOST_CHECK(Fixture::store_callback_called);
+    BOOST_CHECK(!Fixture::get_callback_called);
+}
diff --git a/tests/code/MoveSCP.cpp b/tests/code/MoveSCP.cpp
index e6f261d..2fc6c5f 100644
--- a/tests/code/MoveSCP.cpp
+++ b/tests/code/MoveSCP.cpp
@@ -51,7 +51,7 @@ public:
             {"1.2.826.0.1.3680043.9.5560.3127449359877365688774406533090568532"});
         data_set_1.add("PatientName", {"Hello^World"});
         data_set_1.add("PatientID", {"1234"});
-        this->_responses.push_back(data_set_1);
+        this->_responses.push_back(std::move(data_set_1));
 
         odil::DataSet data_set_2;
         data_set_2.add("SOPClassUID", {odil::registry::RawDataStorage});
@@ -60,7 +60,7 @@ public:
             {"1.2.826.0.1.3680043.9.5560.3221615743193123463515381981101110692"});
         data_set_2.add("PatientName", {"Doe^John"});
         data_set_2.add("PatientID", {"5678"});
-        this->_responses.push_back(data_set_2);
+        this->_responses.push_back(std::move(data_set_2));
 
         this->_response_iterator = this->_responses.begin();
     }
@@ -72,7 +72,7 @@ public:
 
     virtual odil::DataSet get() const
     {
-        return *this->_response_iterator;
+        return *std::make_move_iterator(this->_response_iterator);
     }
 
     virtual void next()
@@ -120,8 +120,8 @@ public:
     }
 
 private:
-    std::vector<odil::DataSet> _responses;
-    std::vector<odil::DataSet>::const_iterator _response_iterator;
+    mutable std::vector<odil::DataSet> _responses;
+    std::vector<odil::DataSet>::iterator _response_iterator;
     odil::Association & _association;
 };
 
@@ -139,8 +139,8 @@ void run_server(Status * status)
         move_scp.set_generator(generator);
 
         // Get move message
-        auto const message = association.receive_message();
-        move_scp(message);
+        auto message = association.receive_message();
+        move_scp(std::move(message));
         // Should throw with peer closing connection
         association.receive_message();
     }
@@ -185,8 +185,8 @@ void run_client(Status * status)
         }
 
         std::ifstream stream(it->path().string());
-        auto const data_set = odil::Reader::read_file(stream).second;
-        status->responses.push_back(data_set);
+        auto data_set = odil::Reader::read_file(stream).second;
+        status->responses.push_back(std::move(data_set));
 
         boost::filesystem::remove(it->path());
     }
diff --git a/tests/code/MoveSCU.cpp b/tests/code/MoveSCU.cpp
index 59c8fec..bb9d3bc 100644
--- a/tests/code/MoveSCU.cpp
+++ b/tests/code/MoveSCU.cpp
@@ -84,6 +84,22 @@ BOOST_FIXTURE_TEST_CASE(Move, Fixture)
             odil::Value::Strings{"2.25.95090344942250266709587559073467305647"});
 }
 
+BOOST_FIXTURE_TEST_CASE(MoveMove, Fixture)
+{
+    odil::MoveSCU scu(this->association);
+    scu.set_move_destination("LOCAL");
+    scu.set_incoming_port(11113);
+
+    scu.set_affected_sop_class(
+        odil::registry::PatientRootQueryRetrieveInformationModelMOVE);
+    auto const results = scu.move(std::move(this->query));
+
+    BOOST_REQUIRE_EQUAL(results.size(), 1);
+    BOOST_CHECK(
+        results[0].as_string("SOPInstanceUID") ==
+            odil::Value::Strings{"2.25.95090344942250266709587559073467305647"});
+}
+
 BOOST_FIXTURE_TEST_CASE(MoveBothCallback, Fixture)
 {
     odil::MoveSCU scu(this->association);
@@ -98,6 +114,22 @@ BOOST_FIXTURE_TEST_CASE(MoveBothCallback, Fixture)
     BOOST_CHECK(Fixture::move_callback_called);
 }
 
+BOOST_FIXTURE_TEST_CASE(MoveBothCallbackMove, Fixture)
+{
+    odil::MoveSCU scu(this->association);
+    scu.set_move_destination("LOCAL");
+    scu.set_incoming_port(11113);
+
+    scu.set_affected_sop_class(
+        odil::registry::PatientRootQueryRetrieveInformationModelMOVE);
+    scu.move(
+        std::move(this->query), Fixture::store_callback,
+        Fixture::move_callback);
+
+    BOOST_CHECK(Fixture::store_callback_called);
+    BOOST_CHECK(Fixture::move_callback_called);
+}
+
 BOOST_FIXTURE_TEST_CASE(MoveOnlyStoreCallback, Fixture)
 {
     odil::MoveSCU scu(this->association);
@@ -112,3 +144,19 @@ BOOST_FIXTURE_TEST_CASE(MoveOnlyStoreCallback, Fixture)
     BOOST_CHECK(Fixture::store_callback_called);
     BOOST_CHECK(!Fixture::move_callback_called);
 }
+
+BOOST_FIXTURE_TEST_CASE(MoveOnlyStoreCallbackMove, Fixture)
+{
+    odil::MoveSCU scu(this->association);
+    scu.set_move_destination("LOCAL");
+    scu.set_incoming_port(11113);
+
+    scu.set_affected_sop_class(
+        odil::registry::PatientRootQueryRetrieveInformationModelMOVE);
+    scu.move(
+        std::move(this->query), Fixture::store_callback,
+        odil::MoveSCU::MoveCallback());
+
+    BOOST_CHECK(Fixture::store_callback_called);
+    BOOST_CHECK(!Fixture::move_callback_called);
+}
diff --git a/tests/code/NCreateSCP.cpp b/tests/code/NCreateSCP.cpp
new file mode 100644
index 0000000..5fcda09
--- /dev/null
+++ b/tests/code/NCreateSCP.cpp
@@ -0,0 +1,91 @@
+#define BOOST_TEST_MODULE NCreateSCP
+#include <boost/test/unit_test.hpp>
+
+#include <chrono>
+#include <cstdlib>
+#include <thread>
+
+#include <boost/asio.hpp>
+
+#include "odil/Association.h"
+#include "odil/NCreateSCP.h"
+#include "odil/Exception.h"
+#include "odil/message/NCreateRequest.h"
+#include "odil/message/Response.h"
+
+struct Status
+{
+    int client;
+    std::string server;
+    bool called;
+};
+
+void run_server(Status * status)
+{
+    odil::Association association;
+    association.set_tcp_timeout(boost::posix_time::seconds(1));
+
+    try
+    {
+        association.receive_association(boost::asio::ip::tcp::v4(), 11113);
+
+        odil::NCreateSCP NCreate_scp(association,
+            [status](odil::message::NCreateRequest const &)
+            {
+                status->called = true;
+                return odil::message::Response::Success;
+            });
+
+        // Get NCreate message
+        auto const message = association.receive_message();
+        NCreate_scp(message);
+        // Should throw with peer closing connection
+        association.receive_message();
+    }
+    catch(odil::AssociationAborted const &)
+    {
+        status->server = "abort";
+    }
+    catch(odil::AssociationReleased const &)
+    {
+        status->server = "release";
+    }
+    catch(odil::Exception const &)
+    {
+        status->server = "Other Odil exception";
+    }
+    catch(...)
+    {
+        status->server = "Other exception";
+    }
+}
+
+// void run_client(Status * status, bool use_abort)
+// {
+//     std::string command = "NCreatescu -ll error";
+//     if(use_abort)
+//     {
+//         command += " --abort";
+//     }
+//     command += " 127.0.0.1 11113";
+//     status->client = system(command.c_str());
+// }
+
+BOOST_AUTO_TEST_CASE(Callback)
+{
+    odil::Association association;
+    odil::NCreateSCP scp(association);
+
+    bool called = false;
+    auto const callback =
+        [&called](odil::message::NCreateRequest const &)
+        {
+            called = true;
+            return odil::message::Response::Success;
+        };
+
+    scp.set_callback(callback);
+    scp.get_callback()(odil::message::NCreateRequest(1, ""));
+    BOOST_REQUIRE_EQUAL(called, true);
+}
+
diff --git a/tests/code/Reader.cpp b/tests/code/Reader.cpp
index 78395bd..96004f9 100644
--- a/tests/code/Reader.cpp
+++ b/tests/code/Reader.cpp
@@ -84,7 +84,7 @@ void do_test(odil::Tag const & tag, odil::VR vr, std::initializer_list<T> const
 {
     // Empty element
     {
-        odil::Element element(odil::Value(), vr);
+        odil::Element element(std::initializer_list<T>(), vr);
         odil::DataSet data_set;
         data_set.add(tag, element);
         do_test(data_set);
diff --git a/tests/code/SCPDispatcher.cpp b/tests/code/SCPDispatcher.cpp
index 3280232..2a92f68 100644
--- a/tests/code/SCPDispatcher.cpp
+++ b/tests/code/SCPDispatcher.cpp
@@ -127,6 +127,6 @@ BOOST_AUTO_TEST_CASE(DispatchWithoutEcho)
     client.join();
 
     BOOST_REQUIRE_EQUAL(status.client, 0);
-    BOOST_REQUIRE_EQUAL(status.server, "No such provider");
+    BOOST_REQUIRE_EQUAL(status.server, "No provider for: 0030");
     BOOST_REQUIRE_EQUAL(status.called, false);
 }
diff --git a/tests/code/StoreSCU.cpp b/tests/code/StoreSCU.cpp
index f4f6c86..dc75521 100644
--- a/tests/code/StoreSCU.cpp
+++ b/tests/code/StoreSCU.cpp
@@ -64,3 +64,11 @@ BOOST_FIXTURE_TEST_CASE(Store, Fixture)
     scu.set_affected_sop_class(this->dataset);
     scu.store(this->dataset);
 }
+
+BOOST_FIXTURE_TEST_CASE(StoreMove, Fixture)
+{
+    odil::StoreSCU scu(this->association);
+
+    scu.set_affected_sop_class(this->dataset);
+    scu.store(std::move(this->dataset));
+}
diff --git a/tests/code/Value.cpp b/tests/code/Value.cpp
index 3365305..f41f8a8 100644
--- a/tests/code/Value.cpp
+++ b/tests/code/Value.cpp
@@ -2,232 +2,101 @@
 #include <boost/test/unit_test.hpp>
 
 #include "odil/DataSet.h"
-#include "odil/Element.h"
 #include "odil/Exception.h"
+#include "odil/Value.h"
 
-BOOST_AUTO_TEST_CASE(Empty)
+template<typename TContainer>
+void test_contents(
+    odil::Value const & value,
+    TContainer const & contents, odil::Value::Type type,
+    TContainer const & (odil::Value::*getter)() const)
 {
-    odil::Value const value;
+    BOOST_CHECK(value.get_type() == type);
+    BOOST_CHECK_EQUAL(value.empty(), contents.empty());
+    BOOST_CHECK_EQUAL(value.size(), contents.size());
+    BOOST_CHECK((value.*getter)() == contents);
 
-    BOOST_CHECK(value.get_type() == odil::Value::Type::Empty);
-    BOOST_CHECK_THROW(value.as_integers(), odil::Exception);
-    BOOST_CHECK_THROW(value.as_reals(), odil::Exception);
-    BOOST_CHECK_THROW(value.as_strings(), odil::Exception);
-    BOOST_CHECK_THROW(value.as_data_sets(), odil::Exception);
-}
-
-BOOST_AUTO_TEST_CASE(Integers)
-{
-    odil::Value const value({1234});
-
-    BOOST_CHECK(value.get_type() == odil::Value::Type::Integers);
-    BOOST_CHECK(value.as_integers() == odil::Value::Integers({1234}));
-
-    BOOST_CHECK_THROW(value.as_reals(), odil::Exception);
-    BOOST_CHECK_THROW(value.as_strings(), odil::Exception);
-    BOOST_CHECK_THROW(value.as_data_sets(), odil::Exception);
-}
-
-BOOST_AUTO_TEST_CASE(ModifyIntegers)
-{
-    odil::Value value({1234});
-    value.as_integers().push_back(5678);
-    BOOST_CHECK(value.as_integers() == odil::Value::Integers({1234, 5678}));
-}
-
-BOOST_AUTO_TEST_CASE(Reals)
-{
-    odil::Value const value({12.34});
-
-    BOOST_CHECK(value.get_type() == odil::Value::Type::Reals);
-    BOOST_CHECK(value.as_reals() == odil::Value::Reals({12.34}));
-
-    BOOST_CHECK_THROW(value.as_integers(), odil::Exception);
-    BOOST_CHECK_THROW(value.as_strings(), odil::Exception);
-    BOOST_CHECK_THROW(value.as_data_sets(), odil::Exception);
-}
-
-BOOST_AUTO_TEST_CASE(ModifyReals)
-{
-    odil::Value value({12.34});
-    value.as_reals().push_back(56.78);
-    BOOST_CHECK(value.as_reals() == odil::Value::Reals({12.34, 56.78}));
-}
-
-BOOST_AUTO_TEST_CASE(Strings)
-{
-    odil::Value const value({"foo"});
-
-    BOOST_CHECK(value.get_type() == odil::Value::Type::Strings);
-    BOOST_CHECK(value.as_strings() == odil::Value::Strings({"foo"}));
-
-    BOOST_CHECK_THROW(value.as_integers(), odil::Exception);
-    BOOST_CHECK_THROW(value.as_reals(), odil::Exception);
-    BOOST_CHECK_THROW(value.as_data_sets(), odil::Exception);
-}
-
-BOOST_AUTO_TEST_CASE(ModifyStrings)
-{
-    odil::Value value({"foo"});
-    value.as_strings().push_back("bar");
-    BOOST_CHECK(value.as_strings() == odil::Value::Strings({"foo", "bar"}));
-}
-
-BOOST_AUTO_TEST_CASE(DataSets)
-{
-    odil::DataSet data_set;
-    data_set.add("PatientID", {"DJ1234"});
-    odil::Value const value({data_set});
-
-    BOOST_CHECK(value.get_type() == odil::Value::Type::DataSets);
-    BOOST_CHECK_EQUAL(value.as_data_sets().size(), 1);
-    BOOST_CHECK(value.as_data_sets()[0].has("PatientID"));
-    BOOST_CHECK(
-        value.as_data_sets()[0].as_string("PatientID") ==
-            odil::Value::Strings({"DJ1234"}));
-
-    BOOST_CHECK_THROW(value.as_integers(), odil::Exception);
-    BOOST_CHECK_THROW(value.as_strings(), odil::Exception);
-    BOOST_CHECK_THROW(value.as_reals(), odil::Exception);
-}
-
-BOOST_AUTO_TEST_CASE(ModifyDataSets)
-{
-    odil::DataSet data_set;
-    data_set.add("PatientID");
-    data_set.as_string("PatientID").push_back("DJ1234");
-    odil::Value value({data_set});
-
-    value.as_data_sets()[0].as_string("PatientID")[0] = "XXX";
-
-    BOOST_CHECK(value.get_type() == odil::Value::Type::DataSets);
-    BOOST_CHECK_EQUAL(value.as_data_sets().size(), 1);
-    BOOST_CHECK(value.as_data_sets()[0].has("PatientID"));
-    BOOST_CHECK(
-        value.as_data_sets()[0].as_string("PatientID") ==
-            odil::Value::Strings({"XXX"}));
-}
-
-BOOST_AUTO_TEST_CASE(EqualityEmpty)
-{
-    odil::Value const value1;
-    odil::Value const value2;
-    odil::Value const value3((odil::Value::Integers()));
-    BOOST_CHECK(value1 == value2);
-    BOOST_CHECK( ! (value1 == value3));
-}
-
-BOOST_AUTO_TEST_CASE(EqualityIntegers)
-{
-    odil::Value const value1({1,2});
-    odil::Value const value2({1,2});
-    odil::Value const value3({3,4});
-    odil::Value const value4({3,4});
-    BOOST_CHECK(value1 == value2);
-    BOOST_CHECK( ! (value1 == value3));
-    BOOST_CHECK( ! (value1 == value4));
-}
-
-BOOST_AUTO_TEST_CASE(EqualityReals)
-{
-    odil::Value const value1({1,2});
-    odil::Value const value2({1,2});
-    odil::Value const value3({3,4});
-    odil::Value const value4({3,4});
-    BOOST_CHECK(value1 == value2);
-    BOOST_CHECK( ! (value1 == value3));
-    BOOST_CHECK( ! (value1 == value4));
-}
-
-BOOST_AUTO_TEST_CASE(EqualityStrings)
-{
-    odil::Value const value1({"1","2"});
-    odil::Value const value2({"1","2"});
-    odil::Value const value3({"3","4"});
-    odil::Value const value4({3,4});
-    BOOST_CHECK(value1 == value2);
-    BOOST_CHECK( ! (value1 == value3));
-    BOOST_CHECK( ! (value1 == value4));
+    if(type != odil::Value::Type::Integers)
+    {
+        BOOST_CHECK_THROW(value.as_integers(), odil::Exception);
+    }
+    if(type != odil::Value::Type::Reals)
+    {
+        BOOST_CHECK_THROW(value.as_reals(), odil::Exception);
+    }
+    if(type != odil::Value::Type::Strings)
+    {
+        BOOST_CHECK_THROW(value.as_strings(), odil::Exception);
+    }
+    if(type != odil::Value::Type::DataSets)
+    {
+        BOOST_CHECK_THROW(value.as_data_sets(), odil::Exception);
+    }
+    if(type != odil::Value::Type::Binary)
+    {
+        BOOST_CHECK_THROW(value.as_binary(), odil::Exception);
+    }
 }
 
-BOOST_AUTO_TEST_CASE(EqualityDataSets)
+template<typename TContainer>
+void test_container(
+    TContainer const & contents, odil::Value::Type type, 
+    TContainer const & (odil::Value::*getter)() const)
 {
-    odil::DataSet dataset1;
-    dataset1.add("PatientID", {"DJ1234"});
-    dataset1.add("PixelSpacing", {1.5, 2.5});
-
-    odil::DataSet dataset2;
-    dataset1.add("PatientName", {"Doe^John"});
-    dataset1.add("PatientAge", {"042Y"});
+    odil::Value const value(contents);
+    test_contents(value, contents, type, getter);
 
-    odil::Value const value1({dataset1});
-    odil::Value const value2({dataset1});
-    odil::Value const value3({dataset2});
-    odil::Value const value4({3,4});
-    BOOST_CHECK(value1 == value2);
-    BOOST_CHECK( ! (value1 == value3));
-    BOOST_CHECK( ! (value1 == value4));
-}
-
-BOOST_AUTO_TEST_CASE(DifferenceEmpty)
-{
-    odil::Value const value1;
-    odil::Value const value2;
-    odil::Value const value3((odil::Value::Integers()));
-    BOOST_CHECK(! (value1 != value2));
-    BOOST_CHECK(value1 != value3);
+    auto contents_copy(contents);
+    odil::Value const other_value(std::move(contents_copy));
+    BOOST_CHECK(contents_copy.empty());
 }
 
-BOOST_AUTO_TEST_CASE(DifferenceIntegers)
+template<typename TContainer>
+void test_initializer_list(
+    std::initializer_list<typename TContainer::value_type> const & contents,
+    odil::Value::Type type, 
+    TContainer const & (odil::Value::*getter)() const)
 {
-    odil::Value const value1({1,2});
-    odil::Value const value2({1,2});
-    odil::Value const value3({3,4});
-    odil::Value const value4({3,4});
-    BOOST_CHECK(! (value1 != value2));
-    BOOST_CHECK(value1 != value3);
-    BOOST_CHECK(value1 != value4);
+    odil::Value const value(contents);
+    test_contents(value, TContainer(contents), type, getter);
 }
 
-BOOST_AUTO_TEST_CASE(DifferenceReals)
+template<typename TContainer>
+void test_modify(
+    TContainer const & contents,
+    TContainer const & (odil::Value::*getter)() const,
+    TContainer & (odil::Value::*setter)())
 {
-    odil::Value const value1({1.,2.});
-    odil::Value const value2({1.,2.});
-    odil::Value const value3({3.,4.});
-    odil::Value const value4({3,4});
-    BOOST_CHECK(! (value1 != value2));
-    BOOST_CHECK(value1 != value3);
-    BOOST_CHECK(value1 != value4);
+    odil::Value value(TContainer{{contents[0]}});
+    (value.*setter)().push_back(contents[1]);
+    BOOST_CHECK((value.*getter)() == contents);
 }
 
-BOOST_AUTO_TEST_CASE(DifferenceStrings)
+template<typename TContainer>
+void test_clear(TContainer const & contents, odil::Value::Type type) 
 {
-    odil::Value const value1({"1","2"});
-    odil::Value const value2({"1","2"});
-    odil::Value const value3({"3","4"});
-    odil::Value const value4({3,4});
-    BOOST_CHECK(! (value1 != value2));
-    BOOST_CHECK(value1 != value3);
-    BOOST_CHECK(value1 != value4);
+    odil::Value value(contents);
+    value.clear();
+    BOOST_CHECK(value.get_type() == type);
+    BOOST_CHECK(value.empty());
 }
 
-BOOST_AUTO_TEST_CASE(DifferenceDataSets)
+template<typename TContainer>
+void test_equality(
+    TContainer const & contents_1, TContainer const & contents_2)
 {
-    odil::DataSet dataset1;
-    dataset1.add("PatientID", {"DJ1234"});
-    dataset1.add("PixelSpacing", {1.5, 2.5});
+    odil::Value const value_1(contents_1);
+    odil::Value const value_2(contents_1);
+    odil::Value const value_3(contents_2);
+    odil::Value const value_4(contents_2);
 
-    odil::DataSet dataset2;
-    dataset1.add("PatientName", {"Doe^John"});
-    dataset1.add("PatientAge", {"042Y"});
+    BOOST_CHECK(value_1 == value_2);
+    BOOST_CHECK( ! (value_1 == value_3));
+    BOOST_CHECK( ! (value_1 == value_4));
 
-    odil::Value const value1({dataset1});
-    odil::Value const value2({dataset1});
-    odil::Value const value3({dataset2});
-    odil::Value const value4({3,4});
-    BOOST_CHECK(! (value1 != value2));
-    BOOST_CHECK(value1 != value3);
-    BOOST_CHECK(value1 != value4);
+    BOOST_CHECK(! (value_1 != value_2));
+    BOOST_CHECK(value_1 != value_3);
+    BOOST_CHECK(value_1 != value_4);
 }
 
 struct Visitor
@@ -241,50 +110,94 @@ struct Visitor
     }
 };
 
-BOOST_AUTO_TEST_CASE(VisitorEmpty)
+template<typename TContainer>
+void test_visitor(
+    TContainer const & contents)
 {
-    odil::Value const value;
-    BOOST_CHECK_THROW(
+    odil::Value const value(contents);
+    BOOST_REQUIRE_EQUAL(
         odil::apply_visitor(Visitor(), value),
-        odil::Exception);
+        typeid(TContainer).name());
 }
 
-BOOST_AUTO_TEST_CASE(VisitorIntegers)
+template<typename TContainer>
+void test(
+    std::initializer_list<typename TContainer::value_type> const & contents,
+    std::initializer_list<typename TContainer::value_type> const & other_contents,
+    odil::Value::Type type, 
+    TContainer const & (odil::Value::*getter)() const,
+    TContainer & (odil::Value::*setter)())
 {
-    odil::Value const value({1234});
-    BOOST_REQUIRE_EQUAL(
-        odil::apply_visitor(Visitor(), value),
-        typeid(odil::Value::Integers).name());
+    TContainer const container(contents);
+    TContainer const other_container(other_contents);
+
+    test_container(TContainer(), type, getter);
+    test_container(container, type, getter);
+    test_initializer_list(contents, type, getter);
+
+    test_modify(container, getter, setter);
+
+    test_clear(container, type);
+
+    test_equality(container, other_container);
+
+    test_visitor(container);
+}
+ 
+BOOST_AUTO_TEST_CASE(Integers)
+{
+    BOOST_CHECK(
+        odil::Value({1234, 5678}).get_type() == odil::Value::Type::Integers);
+    test<odil::Value::Integers>(
+        {1234, 5678}, {9012, 3456},
+        odil::Value::Type::Integers,
+        &odil::Value::as_integers, &odil::Value::as_integers);
 }
 
-BOOST_AUTO_TEST_CASE(VisitorReals)
+BOOST_AUTO_TEST_CASE(Reals)
 {
-    odil::Value const value({12.34});
-    BOOST_REQUIRE_EQUAL(
-        odil::apply_visitor(Visitor(), value),
-        typeid(odil::Value::Reals).name());
+    BOOST_CHECK(
+        odil::Value({12.34, 56.78}).get_type() == odil::Value::Type::Reals);
+    test<odil::Value::Reals>(
+        {12.34, 56.78}, {1., 2.},
+        odil::Value::Type::Reals,
+        &odil::Value::as_reals, &odil::Value::as_reals);
 }
 
-BOOST_AUTO_TEST_CASE(VisitorStrings)
+BOOST_AUTO_TEST_CASE(Strings)
 {
-    odil::Value const value({"foo"});
-    BOOST_REQUIRE_EQUAL(
-        odil::apply_visitor(Visitor(), value),
-        typeid(odil::Value::Strings).name());
+    BOOST_CHECK(
+        odil::Value({"foo", "bar"}).get_type() == odil::Value::Type::Strings);
+    test<odil::Value::Strings>(
+        {"foo", "bar"}, {"plip", "plop"},
+        odil::Value::Type::Strings,
+        &odil::Value::as_strings, &odil::Value::as_strings);
 }
 
-BOOST_AUTO_TEST_CASE(VisitorDataSets)
+BOOST_AUTO_TEST_CASE(DataSets)
 {
-    odil::Value const value({odil::DataSet()});
-    BOOST_REQUIRE_EQUAL(
-        odil::apply_visitor(Visitor(), value),
-        typeid(odil::Value::DataSets).name());
+    odil::DataSet data_set_1;
+    data_set_1.add("PatientID", {"DJ1234"});
+
+    odil::DataSet data_set_2;
+    data_set_2.add("EchoTime", {100});
+
+    BOOST_CHECK(
+        odil::Value({data_set_1, data_set_2}).get_type()
+            == odil::Value::Type::DataSets);
+    test<odil::Value::DataSets>(
+        {data_set_1, data_set_2}, {data_set_2, data_set_1},
+        odil::Value::Type::DataSets,
+        &odil::Value::as_data_sets, &odil::Value::as_data_sets);
 }
 
-BOOST_AUTO_TEST_CASE(VisitorBinary)
+BOOST_AUTO_TEST_CASE(Binary)
 {
-    odil::Value const value((odil::Value::Binary()));
-    BOOST_REQUIRE_EQUAL(
-        odil::apply_visitor(Visitor(), value),
-        typeid(odil::Value::Binary).name());
+    BOOST_CHECK(
+        odil::Value({{0x1, 0x2}, {0x3}}).get_type()
+            == odil::Value::Type::Binary);
+    test<odil::Value::Binary>(
+        {{0x1, 0x2}, {0x3}}, {{0x4}, {0x5, 0x6}},
+        odil::Value::Type::Binary,
+        &odil::Value::as_binary, &odil::Value::as_binary);
 }
diff --git a/tests/run b/tests/run
index e426ca4..ed3ea71 100755
--- a/tests/run
+++ b/tests/run
@@ -14,9 +14,15 @@ def main():
         description="Run all tests (C++ and Python). "
             "This program must be run from the build directory.")
     parser.add_argument("tests", nargs="*", help="Python only")
-    parser.add_argument("--no-network", action="store_true")
+    parser.add_argument(
+        "--no-network", action="store_true",
+        help="Don't run network-related tests")
+    parser.add_argument(
+        "--verbose", "-v", action="store_true", help="Increase tests verbosity")
     parser.add_argument("--test-regex", "-R", help="C++ only")
     parser.add_argument("--exclude-regex", "-E", help="C++ only")
+    parser.add_argument(
+        "--nose", default="nosetests", help="nosetests executable")
     parser.add_argument("--exclude", "-e", help="Python only")
     arguments = parser.parse_args()
 
@@ -67,12 +73,16 @@ def main():
                 source_directory = match.group(1)
                 break
 
-    cpp_code = subprocess.call(
-        ["ctest", "--no-compress-output", "-T", "Test"]+cpp_args,
-        env=environment)
-    python_code = subprocess.call(
-        ["nosetests-2.7", os.path.join(source_directory, "tests", "wrappers")]+python_args,
-        env=environment)
+    ctest = ["ctest", "--no-compress-output", "-T", "Test"]
+    if arguments.verbose:
+        ctest.append("-V")
+    cpp_code = subprocess.call(ctest+cpp_args, env=environment)
+
+    nose = [
+        arguments.nose, os.path.join(source_directory, "tests", "wrappers")]
+    if arguments.verbose:
+        nose.append("-v")
+    python_code = subprocess.call(nose+python_args, env=environment)
 
     tearDown(directory, process)
 
diff --git a/tests/tools/CMakeLists.txt b/tests/tools/CMakeLists.txt
index 35b445e..e3ec9c1 100644
--- a/tests/tools/CMakeLists.txt
+++ b/tests/tools/CMakeLists.txt
@@ -7,6 +7,8 @@ link_directories(${DCMTK_LIBRARY_DIRS})
 file(GLOB headers *.h)
 file(GLOB files "*.cc")
 
-add_executable(dcmtk_getscu ${files} ${headers})
-target_link_libraries(dcmtk_getscu ${DCMTK_LIBRARIES})
-set_target_properties(dcmtk_getscu PROPERTIES OUTPUT_NAME getscu)
+if(USE_BUILTIN_DCMTK_GETSCU)
+    add_executable(dcmtk_getscu ${files} ${headers})
+    target_link_libraries(dcmtk_getscu ${DCMTK_LIBRARIES})
+    set_target_properties(dcmtk_getscu PROPERTIES OUTPUT_NAME getscu)
+endif()
diff --git a/tests/wrappers/test_association_parameters.py b/tests/wrappers/test_association_parameters.py
index 721f77b..455ea4b 100644
--- a/tests/wrappers/test_association_parameters.py
+++ b/tests/wrappers/test_association_parameters.py
@@ -12,7 +12,7 @@ class TestAssociationParameters(unittest.TestCase):
         user_identity = parameters.get_user_identity()
         self.assertEqual(
             user_identity.type, 
-            odil.AssociationParameters.UserIdentity.Type.None)
+            getattr(odil.AssociationParameters.UserIdentity.Type, "None"))
 
         self.assertEqual(parameters.get_maximum_length(), 16384)
 
@@ -85,7 +85,7 @@ class TestAssociationParameters(unittest.TestCase):
         user_identity = parameters.get_user_identity()
         self.assertEqual(
             user_identity.type, 
-            odil.AssociationParameters.UserIdentity.Type.None)
+            getattr(odil.AssociationParameters.UserIdentity.Type, "None"))
 
     def test_maximum_length(self):
         parameters = odil.AssociationParameters()
diff --git a/tests/wrappers/test_data_set.py b/tests/wrappers/test_data_set.py
index 8e4a20e..12fba09 100644
--- a/tests/wrappers/test_data_set.py
+++ b/tests/wrappers/test_data_set.py
@@ -36,154 +36,118 @@ class TestDataSet(unittest.TestCase):
         self.assertTrue(data_set.empty(tag))
         self.assertEqual(data_set.size(tag), 0)
 
-    def test_int_element(self):
-        tag = odil.registry.SelectorUSValue
-        value = [1, 2, 3]
+    def _test_sequences(self, odil_contents, python_contents):
+        if python_contents and isinstance(python_contents[0], bytearray):
+            self.assertSequenceEqual(
+                [bytearray([x for x in item]) for item in odil_contents], 
+                python_contents)
+        else:
+            self.assertSequenceEqual(list(odil_contents), list(python_contents))
+
+    def _test_element_value(
+            self, data_set, tag, contents, type_check, accessor):
+        self.assertTrue(data_set.has(tag))
+        self.assertEqual(data_set.empty(tag), len(contents)==0)
+        self.assertEqual(data_set.size(tag), len(contents))
+        self.assertTrue(type_check(data_set, tag))
+        self._test_sequences(accessor(data_set, tag), contents)
+
+        if not data_set.is_int(tag):
+            with self.assertRaises(odil.Exception):
+                data_set.as_int(tag)
+        if not data_set.is_real(tag):
+            with self.assertRaises(odil.Exception):
+                data_set.as_real(tag)
+        if not data_set.is_string(tag):
+            with self.assertRaises(odil.Exception):
+                data_set.as_string(tag)
+        if not data_set.is_data_set(tag):
+            with self.assertRaises(odil.Exception):
+                data_set.as_data_set(tag)
+        if not data_set.is_binary(tag):
+            with self.assertRaises(odil.Exception):
+                data_set.as_binary(tag)
+
+    def _test_implicit_container(
+            self, tag, empty_content, type_check, accessor):
         data_set = odil.DataSet()
-        data_set.add(tag, value)
-
-        self.assertFalse(data_set.empty())
-        self.assertEqual(data_set.size(), 1)
-        self.assertEqual(len(data_set), 1)
-
-        self.assertEqual(data_set.get_vr(tag), odil.VR.US)
-        self.assertFalse(data_set.empty(tag))
-        self.assertEqual(data_set.size(tag), 3)
-        self.assertTrue(data_set.is_int(tag))
-        self.assertSequenceEqual(data_set.as_int(tag), value)
-
-        value = [4, 5]
-        data_set.set(tag, value)
-
-        self.assertFalse(data_set.empty())
-        self.assertEqual(data_set.size(), 1)
-        self.assertEqual(len(data_set), 1)
-
-        self.assertEqual(data_set.get_vr(tag), odil.VR.US)
-        self.assertFalse(data_set.empty(tag))
-        self.assertEqual(data_set.size(tag), len(value))
-        self.assertTrue(data_set.is_int(tag))
-        self.assertSequenceEqual(data_set.as_int(tag), value)
+        data_set.add(tag)
+        self._test_element_value(
+            data_set, tag, empty_content, type_check, accessor)
 
-    def test_real_element(self):
-        tag = odil.registry.SelectorFLValue
-        value = [1.1, 2, 3.3]
+    def _test_container_no_vr(self, tag, contents, type_check, accessor):
         data_set = odil.DataSet()
-        data_set.add(tag, value)
-
-        self.assertFalse(data_set.empty())
-        self.assertEqual(data_set.size(), 1)
-        self.assertEqual(len(data_set), 1)
-
-        self.assertEqual(data_set.get_vr(tag), odil.VR.FL)
-        self.assertFalse(data_set.empty(tag))
-        self.assertEqual(data_set.size(tag), 3)
-        self.assertTrue(data_set.is_real(tag))
-        self.assertSequenceEqual(data_set.as_real(tag), value)
-
-        value = [4.4, 5]
-        data_set.set(tag, value)
-
-        self.assertFalse(data_set.empty())
-        self.assertEqual(data_set.size(), 1)
-        self.assertEqual(len(data_set), 1)
-
-        self.assertEqual(data_set.get_vr(tag), odil.VR.FL)
-        self.assertFalse(data_set.empty(tag))
-        self.assertEqual(data_set.size(tag), len(value))
-        self.assertTrue(data_set.is_real(tag))
-        self.assertSequenceEqual(data_set.as_real(tag), value)
+        data_set.add(tag, contents)
+        self._test_element_value(data_set, tag, contents, type_check, accessor)
 
-    def test_string_element(self):
-        tag = odil.registry.SelectorCSValue
-        value = ["foo", "bar"]
+    def _test_container_vr(self, tag, contents, vr, type_check, accessor):
         data_set = odil.DataSet()
-        data_set.add(tag, value)
-
-        self.assertFalse(data_set.empty())
-        self.assertEqual(data_set.size(), 1)
-        self.assertEqual(len(data_set), 1)
-
-        self.assertEqual(data_set.get_vr(tag), odil.VR.CS)
-        self.assertFalse(data_set.empty(tag))
-        self.assertEqual(data_set.size(tag), 2)
-        self.assertTrue(data_set.is_string(tag))
-        self.assertSequenceEqual(data_set.as_string(tag), value)
-
-        value = ["baz"]
-        data_set.set(tag, value)
-
-        self.assertFalse(data_set.empty())
-        self.assertEqual(data_set.size(), 1)
-        self.assertEqual(len(data_set), 1)
-
-        self.assertEqual(data_set.get_vr(tag), odil.VR.CS)
-        self.assertFalse(data_set.empty(tag))
-        self.assertEqual(data_set.size(tag), len(value))
-        self.assertTrue(data_set.is_string(tag))
-        self.assertSequenceEqual(data_set.as_string(tag), value)
+        data_set.add(tag, contents, vr)
+        self._test_element_value(data_set, tag, contents, type_check, accessor)
 
-    def test_data_set_element(self):
-        tag = odil.registry.SelectorCodeSequenceValue
-        value = [odil.DataSet(), odil.DataSet()]
+    def _test_modify(self, tag, contents, accessor):
         data_set = odil.DataSet()
-        data_set.add(tag, value)
-
-        self.assertFalse(data_set.empty())
-        self.assertEqual(data_set.size(), 1)
-        self.assertEqual(len(data_set), 1)
-
-        self.assertEqual(data_set.get_vr(tag), odil.VR.SQ)
-        self.assertFalse(data_set.empty(tag))
-        self.assertEqual(data_set.size(tag), 2)
-        self.assertTrue(data_set.is_data_set(tag))
-        self.assertSequenceEqual(data_set.as_data_set(tag), value)
-
-        value = [odil.DataSet()]
-        data_set.set(tag, value)
-
-        self.assertFalse(data_set.empty())
-        self.assertEqual(data_set.size(), 1)
-        self.assertEqual(len(data_set), 1)
-
-        self.assertEqual(data_set.get_vr(tag), odil.VR.SQ)
-        self.assertFalse(data_set.empty(tag))
-        self.assertEqual(data_set.size(tag), len(value))
-        self.assertTrue(data_set.is_data_set(tag))
-        self.assertSequenceEqual(data_set.as_data_set(tag), value)
-
-    def test_binary_element(self):
-        tag = odil.registry.RedPaletteColorLookupTableData
-        value = [bytearray("\x01\x02\x03")]
+        data_set.add(tag, [contents[0]])
+        if isinstance(contents[0], bytearray):
+            accessor(data_set, tag).append(
+                odil.Value.BinaryItem("".join(chr(x) for x in contents[1])))
+        else:
+            accessor(data_set, tag).append(contents[1])
+        self._test_sequences(accessor(data_set, tag), contents)
+
+    def _test_clear(self, tag, contents, type_check):
         data_set = odil.DataSet()
-        data_set.add(tag, value)
-
-        self.assertFalse(data_set.empty())
-        self.assertEqual(data_set.size(), 1)
-        self.assertEqual(len(data_set), 1)
-
-        self.assertEqual(data_set.get_vr(tag), odil.VR.OW)
-        self.assertFalse(data_set.empty(tag))
-        self.assertEqual(data_set.size(tag), 1)
-        self.assertTrue(data_set.is_binary(tag))
-        self.assertSequenceEqual(
-            [bytearray([x for x in item]) for item in data_set.as_binary(tag)],
-            value)
-
-        value = [bytearray("\x04\x05")]
-        data_set.set(tag, value)
-
-        self.assertFalse(data_set.empty())
-        self.assertEqual(data_set.size(), 1)
-        self.assertEqual(len(data_set), 1)
+        data_set.add(tag, contents)
+        data_set.clear(tag)
+        self.assertTrue(type_check(data_set, tag))
+        self.assertTrue(data_set.empty(tag))
 
-        self.assertEqual(data_set.get_vr(tag), odil.VR.OW)
-        self.assertFalse(data_set.empty(tag))
-        self.assertEqual(data_set.size(tag), len(value))
-        self.assertTrue(data_set.is_binary(tag))
-        self.assertSequenceEqual(
-            [bytearray([x for x in item]) for item in data_set.as_binary(tag)],
-            value)
+    def _test_element(
+            self, tag, empty_content, contents, vr, type_check, accessor):
+        self._test_implicit_container(
+            tag, empty_content, type_check, accessor)
+
+        self._test_container_no_vr(tag, contents, type_check, accessor)
+        self._test_container_vr(tag, contents, vr, type_check, accessor)
+
+        self._test_modify(tag, contents, accessor)
+
+        self._test_clear(tag, contents, type_check)
+
+    def test_int(self):
+        self._test_element(
+            odil.registry.Rows, odil.Value.Integers(), [1234, 5678], odil.VR.US,
+            odil.DataSet.is_int, odil.DataSet.as_int)
+
+    def test_real(self):
+        self._test_element(
+            odil.registry.SpacingBetweenSlices, odil.Value.Reals(), 
+            [12.34, 56.78], odil.VR.FL,
+            odil.DataSet.is_real, odil.DataSet.as_real)
+
+    def test_string(self):
+        self._test_element(
+            odil.registry.PatientID, odil.Value.Strings(), ["foo", "bar"], 
+            odil.VR.LT,
+            odil.DataSet.is_string, odil.DataSet.as_string)
+
+    def test_data_set(self):
+        data_set_1 = odil.DataSet()
+        data_set_1.add("PatientID", ["DJ1234"])
+    
+        data_set_2 = odil.DataSet()
+        data_set_2.add("EchoTime", [100])
+
+        self._test_element(
+            odil.registry.ReferencedStudySequence, odil.Value.DataSets(), 
+            [data_set_1, data_set_2], odil.VR.SQ,
+            odil.DataSet.is_data_set, odil.DataSet.as_data_set)
+
+    def test_binary(self):
+        self._test_element(
+            odil.registry.BadPixelImage, odil.Value.Binary(), 
+            [bytearray([0x01, 0x02]), bytearray([0x03])], odil.VR.OB,
+            odil.DataSet.is_binary, odil.DataSet.as_binary)
 
     def test_getitem(self):
         data_set = odil.DataSet()
@@ -230,3 +194,4 @@ class TestDataSet(unittest.TestCase):
 
 if __name__ == "__main__":
     unittest.main()
+
diff --git a/tests/wrappers/test_element.py b/tests/wrappers/test_element.py
index 01cac2c..34764e4 100644
--- a/tests/wrappers/test_element.py
+++ b/tests/wrappers/test_element.py
@@ -1,56 +1,156 @@
+import re
 import unittest
 
 import odil
 
 class TestElement(unittest.TestCase):
-    def test_empty_constructor(self):
-        element = odil.Element()
+    def _test_sequences(self, odil_contents, python_contents):
+        if python_contents and isinstance(python_contents[0], bytearray):
+            self.assertSequenceEqual(
+                [bytearray([x for x in item]) for item in odil_contents], 
+                python_contents)
+        else:
+            self.assertSequenceEqual(list(odil_contents), list(python_contents))
+
+    def _test_value(self, element, vr, contents, type_check, accessor):
+        self.assertTrue(type_check(element))
+        self.assertEqual(element.empty(), len(contents)==0)
+        self.assertEqual(element.size(), len(contents))
+        self.assertEqual(len(element), len(contents))
+        self._test_sequences(accessor(element), contents)
+        self.assertEqual(element.vr, vr)
+
+        if not element.is_int():
+            with self.assertRaises(odil.Exception):
+                element.as_int()
+        if not element.is_real():
+            with self.assertRaises(odil.Exception):
+                element.as_real()
+        if not element.is_string():
+            with self.assertRaises(odil.Exception):
+                element.as_string()
+        if not element.is_data_set():
+            with self.assertRaises(odil.Exception):
+                element.as_data_set()
+        if not element.is_binary():
+            with self.assertRaises(odil.Exception):
+                element.as_binary()
+
+    def _test_implicit_container(self, empty_content, vr, type_check, accessor):
+        element = odil.Element(vr)
+        self._test_value(element, vr, empty_content, type_check, accessor)
+
+    def _test_container(self, contents, vr, type_check, accessor):
+        element = odil.Element(contents, vr)
+        self._test_value(element, vr, contents, type_check, accessor)
+    
+    def _test_modify(self, contents, vr, accessor):
+        value = odil.Element([contents[0]], vr)
+        if isinstance(contents[0], bytearray):
+            accessor(value).append(odil.Value.BinaryItem("".join(chr(x) for x in contents[1])))
+        else:
+            accessor(value).append(contents[1])
+        
+        self._test_sequences(accessor(value), contents)
+
+    def _test_clear(self, contents, vr, type_check):
+        element = odil.Element(contents, vr)
+        element.clear()
+        self.assertTrue(type_check(element))
         self.assertTrue(element.empty())
-        self.assertEqual(element.size(), 0)
-        self.assertEqual(len(element), 0)
-        self.assertEqual(element.vr, odil.VR.INVALID)
-
-    def test_integers_constructor(self):
-        items = [1, 2, 3]
-        element = odil.Element(items, odil.VR.US)
-        self.assertSequenceEqual(element.as_int(), items)
-        self.assertEqual(element.vr, odil.VR.US)
-        self.assertEqual(element.size(), 3)
-        self.assertEqual(len(element), 3)
-
-    def test_reals_constructor(self):
-        items = [1.1, 2, 3.3]
-        element = odil.Element(items, odil.VR.FL)
-        self.assertSequenceEqual(element.as_real(), items)
-        self.assertEqual(element.vr, odil.VR.FL)
-        self.assertEqual(element.size(), 3)
-        self.assertEqual(len(element), 3)
-
-    def test_strings_constructor(self):
-        items = ["foo", "bar"]
-        element = odil.Element(items, odil.VR.CS)
-        self.assertSequenceEqual(element.as_string(), items)
-        self.assertEqual(element.vr, odil.VR.CS)
-        self.assertEqual(element.size(), 2)
-        self.assertEqual(len(element), 2)
-
-    def test_data_sets_constructor(self):
-        items = [odil.DataSet(), odil.DataSet()]
-        element = odil.Element(items, odil.VR.SQ)
-        self.assertSequenceEqual(element.as_data_set(), items)
-        self.assertEqual(element.vr, odil.VR.SQ)
-        self.assertEqual(element.size(), 2)
-        self.assertEqual(len(element), 2)
-
-    def test_binary_constructor(self):
-        items = [bytearray("\x01\x02\x03")]
-        element = odil.Element(items, odil.VR.OB)
-        self.assertSequenceEqual(
-            [bytearray([x for x in item]) for item in element.as_binary()], 
-            items)
-        self.assertEqual(element.vr, odil.VR.OB)
-        self.assertEqual(element.size(), 1)
-        self.assertEqual(len(element), 1)
+
+    def _test_equality(self, contents_1, contents_2, vr_1, vr_2):
+        element_1 = odil.Element(contents_1, vr_1)
+        element_2 = odil.Element(contents_1, vr_1)
+        element_3 = odil.Element(contents_1, vr_2)
+        element_4 = odil.Element(contents_2, vr_1)
+
+        self.assertTrue(element_1 == element_2)
+        self.assertFalse(element_1 == element_3)
+        self.assertFalse(element_1 == element_4)
+
+        self.assertFalse(element_1 != element_2)
+        self.assertTrue(element_1 != element_3)
+        self.assertTrue(element_1 != element_4)
+
+    def _test(
+            self, 
+            empty_content, contents, other_contents, vr, other_vr, 
+            type_check, accessor):
+        self._test_implicit_container(empty_content, vr, type_check, accessor)
+        self._test_container(contents, vr, type_check, accessor)
+
+        self._test_modify(contents, vr, accessor)
+
+        self._test_clear(contents, vr, type_check)
+
+        self._test_equality(contents, other_contents, vr, other_vr)
+
+    def test_implicit_type(self):
+        for vr in dir(odil.VR):
+            if re.match(r"^[A-Z]{2}$", vr) is None:
+                continue
+
+            vr = getattr(odil.VR, vr)
+            if odil.is_int(vr):
+                self._test_implicit_container(
+                    odil.Value.Integers(), vr, 
+                    odil.Element.is_int, odil.Element.as_int)
+            elif odil.is_real(vr):
+                self._test_implicit_container(
+                    odil.Value.Reals(), vr, 
+                    odil.Element.is_real, odil.Element.as_real)
+            elif odil.is_string(vr):
+                self._test_implicit_container(
+                    odil.Value.Strings(), vr, 
+                    odil.Element.is_string, odil.Element.as_string)
+            elif vr == odil.VR.SQ:
+                self._test_implicit_container(
+                    odil.Value.DataSets(), vr, 
+                    odil.Element.is_data_set, odil.Element.as_data_set)
+            elif odil.is_binary(vr):
+                self._test_implicit_container(
+                    odil.Value.Binary(), vr, 
+                    odil.Element.is_binary, odil.Element.as_binary)
+
+    def test_integers(self):
+        self._test(
+            odil.Value.Integers(), [1234, 5678], [9012, 3456],
+            odil.VR.US, odil.VR.UL,
+            odil.Element.is_int, odil.Element.as_int)
+
+    def test_reals(self):
+        self._test(
+            odil.Value.Reals(), [12.34, 56.78], [1., 2.],
+            odil.VR.FD, odil.VR.DS,
+            odil.Element.is_real, odil.Element.as_real)
+
+    def test_strings(self):
+        self._test(
+            odil.Value.Strings(), ["foo", "bar"], ["plip", "plop"],
+            odil.VR.CS, odil.VR.UT,
+            odil.Element.is_string, odil.Element.as_string)
+        
+    def test_data_sets(self):
+        data_set_1 = odil.DataSet()
+        data_set_1.add("PatientID", ["DJ1234"])
+    
+        data_set_2 = odil.DataSet()
+        data_set_2.add("EchoTime", [100])
+
+        self._test(
+            odil.Value.DataSets(), 
+            [data_set_1, data_set_2], [data_set_2, data_set_1],
+            odil.VR.SQ, odil.VR.UN,
+            odil.Element.is_data_set, odil.Element.as_data_set)
+
+    def test_binary(self):
+        self._test(
+            odil.Value.Binary(), 
+            [bytearray([0x01, 0x02]), bytearray([0x03])],
+            [bytearray([0x04]), bytearray([0x05, 0x06])],
+            odil.VR.OB, odil.VR.OW,
+            odil.Element.is_binary, odil.Element.as_binary)
 
 if __name__ == "__main__":
     unittest.main()
diff --git a/tests/wrappers/test_tag.py b/tests/wrappers/test_tag.py
index cf21990..4f580e8 100644
--- a/tests/wrappers/test_tag.py
+++ b/tests/wrappers/test_tag.py
@@ -83,5 +83,16 @@ class TestTag(unittest.TestCase):
         tag = odil.Tag(0x1234, 0x5678)
         self.assertEqual(str(tag), "12345678")
 
+    def test_hash(self):
+        tag = odil.Tag(
+            odil.registry.PatientName.group, odil.registry.PatientName.element)
+        d = {tag: True}
+
+        self.assertTrue(tag in d)
+        self.assertTrue(odil.registry.PatientName in d)
+
+        other = odil.Tag(
+            odil.registry.PatientID.group, odil.registry.PatientID.element)
+
 if __name__ == "__main__":
     unittest.main()
diff --git a/tests/wrappers/test_value.py b/tests/wrappers/test_value.py
index b461690..207f3a6 100644
--- a/tests/wrappers/test_value.py
+++ b/tests/wrappers/test_value.py
@@ -1,44 +1,126 @@
+#encoding: utf-8
 import unittest
 
 import odil
 
 class TestValue(unittest.TestCase):
-    def test_empty_constructor(self):
-        value = odil.Value()
-        self.assertEqual(value.type, odil.Value.Type.Empty)
-        self.assertTrue(value.empty)
+    def _test_sequences(self, odil_contents, python_contents):
+        if python_contents and isinstance(python_contents[0], bytearray):
+            self.assertSequenceEqual(
+                [bytearray([x for x in item]) for item in odil_contents], 
+                python_contents)
+        else:
+            self.assertSequenceEqual(list(odil_contents), list(python_contents))
         
-    def test_integers_constructor(self):
-        items = [1, 2, 3]
-        value = odil.Value(items)
-        self.assertEqual(value.type, odil.Value.Type.Integers)
-        self.assertSequenceEqual(value.as_integers(), items)
-    
-    def test_reals_constructor(self):
-        items = [1.1, 2., 3.3]
-        value = odil.Value(items)
-        self.assertEqual(value.type, odil.Value.Type.Reals)
-        self.assertSequenceEqual(value.as_reals(), items)
-    
-    def test_strings_constructor(self):
-        items = ["foo", "bar"]
-        value = odil.Value(items)
-        self.assertEqual(value.type, odil.Value.Type.Strings)
-        self.assertSequenceEqual(value.as_strings(), items)
-    
-    def test_data_sets_constructor(self):
-        items = [odil.DataSet(), odil.DataSet()]
-        value = odil.Value(items)
-        self.assertEqual(value.type, odil.Value.Type.DataSets)
-        self.assertSequenceEqual(value.as_data_sets(), items)
+    def _test_contents(self, value, contents, type_, accessor):
+        self.assertEqual(value.type, type_)
+        self.assertEqual(value.empty(), len(contents) == 0)
+        self.assertEqual(value.size(), len(contents))
+        self.assertEqual(len(value), len(contents))
+        self._test_sequences(accessor(value), contents)
+
+        if type_ != odil.Value.Type.Integers:
+            with self.assertRaises(odil.Exception):
+                value.as_integers()
+        if type_ != odil.Value.Type.Reals:
+            with self.assertRaises(odil.Exception):
+                value.as_reals()
+        if type_ != odil.Value.Type.Strings:
+            with self.assertRaises(odil.Exception):
+                value.as_strings()
+        if type_ != odil.Value.Type.DataSets:
+            with self.assertRaises(odil.Exception):
+                value.as_data_sets()
+        if type_ != odil.Value.Type.Binary:
+            with self.assertRaises(odil.Exception):
+                value.as_binary()
+
+    def _test_container(self, contents, type_, accessor):
+        value = odil.Value(contents)
+        self._test_contents(value, contents, type_, accessor)
+
+    def _test_modify(self, contents, accessor):
+        value = odil.Value([contents[0]])
+        if isinstance(contents[0], bytearray):
+            accessor(value).append(odil.Value.BinaryItem("".join(chr(x) for x in contents[1])))
+        else:
+            accessor(value).append(contents[1])
+        
+        self._test_sequences(accessor(value), contents)
+
+    def _test_clear(self, contents, type_):
+        value = odil.Value(contents)
+        value.clear()
+        self.assertEqual(value.type, type_)
+        self.assertTrue(value.empty())
+
+    def _test_equality(self, contents_1, contents_2):
+        value_1 = odil.Value(contents_1)
+        value_2 = odil.Value(contents_1)
+        value_3 = odil.Value(contents_2)
+        value_4 = odil.Value(contents_2)
+
+        self.assertTrue(value_1 == value_2)
+        self.assertFalse(value_1 == value_3)
+        self.assertFalse(value_1 == value_4)
+
+        self.assertFalse(value_1 != value_2)
+        self.assertTrue(value_1 != value_3)
+        self.assertTrue(value_1 != value_4)
+
+    def _test(self, empty_content, contents, other_contents, type_, accessor):
+        self._test_container(empty_content, type_, accessor)
+        self._test_container(contents, type_, accessor)
+
+        self._test_modify(contents, accessor)
+
+        self._test_clear(contents, type_)
+
+        self._test_equality(contents, other_contents)
+
+    def test_integers(self):
+        self._test(
+            odil.Value.Integers(), [1234, 5678], [9012, 3456], 
+            odil.Value.Type.Integers, odil.Value.as_integers)
+        
+    def test_reals(self):
+        self._test(
+            odil.Value.Reals(), [12.34, 56.78], [1., 2.],
+            odil.Value.Type.Reals, odil.Value.as_reals)
+
+    def test_strings(self):
+        self._test(
+            odil.Value.Strings(), ["foo", "bar"], ["plip", "plop"],
+            odil.Value.Type.Strings, odil.Value.as_strings)
+
+    # FIXME: strings in DICOM are byte-string, with some VR requiring 
+    # conversion based on Specific Character Set. In Boost.Python, some
+    # explicit conversion are used (unicode <-> std::string in Python 3).
+
+    def test_data_sets(self):
+        data_set_1 = odil.DataSet()
+        data_set_1.add("PatientID", ["DJ1234"])
     
-    def test_binary_constructor(self):
-        items = [bytearray("\x01\x02\x03")]
-        value = odil.Value(items)
-        self.assertEqual(value.type, odil.Value.Type.Binary)
-        self.assertSequenceEqual(
-            [bytearray([x for x in item]) for item in value.as_binary()], 
-            items)
+        data_set_2 = odil.DataSet()
+        data_set_2.add("EchoTime", [100])
+
+        self._test(
+            odil.Value.DataSets(), 
+            [data_set_1, data_set_2], [data_set_2, data_set_1],
+            odil.Value.Type.DataSets, odil.Value.as_data_sets)
+
+    def test_binary(self):
+        self._test(
+            odil.Value.Binary(), 
+            [bytearray([0x01, 0x02]), bytearray([0x03])],
+            [bytearray([0x04]), bytearray([0x05, 0x06])],
+            odil.Value.Type.Binary, odil.Value.as_binary)
+        
+    def test_unknown_constructor(self):
+        class Foo(object): pass
+        items = [Foo()]
+        with self.assertRaises(odil.Exception):
+            odil.Value(items)
 
 class TestValueIntegers(unittest.TestCase):
     def test_empty_constructor(self):
diff --git a/tests/wrappers/test_vr.py b/tests/wrappers/test_vr.py
index ef1660f..3254690 100644
--- a/tests/wrappers/test_vr.py
+++ b/tests/wrappers/test_vr.py
@@ -3,6 +3,26 @@ import unittest
 import odil
 
 class TestVR(unittest.TestCase):
+    def test_string_constructor(self):
+        data_set = odil.DataSet()
+        data_set.add(odil.registry.PatientName, ["Doe^John"], "UT")
+        self.assertEqual(data_set.get_vr(odil.registry.PatientName), odil.VR.UT)
+
+    def test_unicode_constructor(self):
+        data_set = odil.DataSet()
+        data_set.add(odil.registry.PatientName, ["Doe^John"], u"UT")
+        self.assertEqual(data_set.get_vr(odil.registry.PatientName), odil.VR.UT)
+
+    def test_invalid_string_constructor(self):
+        data_set = odil.DataSet()
+        with self.assertRaises(Exception):
+            data_set.add(odil.registry.PatientName, ["Doe^John"], "XX")
+
+    def test_invalid_unicode_constructor(self):
+        data_set = odil.DataSet()
+        with self.assertRaises(Exception):
+            data_set.add(odil.registry.PatientName, ["Doe^John"], u"XX")
+
     def test_string(self):
         vr = odil.VR.AE
         self.assertEqual(str(vr), "AE")
diff --git a/wrappers/Assocation.cpp b/wrappers/Assocation.cpp
index 50872b0..4c424ba 100644
--- a/wrappers/Assocation.cpp
+++ b/wrappers/Assocation.cpp
@@ -6,44 +6,48 @@
  * for details.
  ************************************************************************/
 
-#include <boost/asio.hpp>
-#include <boost/python.hpp>
+#include "exception_factory.h"
 
 #include "odil/Association.h"
 
-#include "exception_factory.h"
+#include <boost/asio.hpp>
+#include <boost/python.hpp>
 
 namespace
 {
 
-PyObject * wrapped_AssociationReleased;
-PyObject * wrapped_AssociationAborted;
+PyObject* wrapped_AssociationReleased;
+PyObject* wrapped_AssociationAborted;
 
 void receive_association(
-    odil::Association & association, std::string protocol, unsigned short port)
+    odil::Association& association, std::string protocol, unsigned short port)
 {
     if(protocol == "v4")
     {
         association.receive_association(boost::asio::ip::tcp::v4(), port);
     }
+    else if (protocol == "v6")
+    {
+        association.receive_association(boost::asio::ip::tcp::v6(), port);
+    }
 }
 
-void released_translator(odil::AssociationReleased const & e)
+void released_translator(odil::AssociationReleased const& e)
 {
     PyErr_SetString(wrapped_AssociationReleased, e.what());
 }
 
-void aborted_translator(odil::AssociationAborted const & e)
+void aborted_translator(odil::AssociationAborted const& e)
 {
     PyErr_SetString(wrapped_AssociationAborted, e.what());
 }
 
-float get_tcp_timeout(odil::Association const & association)
+float get_tcp_timeout(odil::Association const& association)
 {
     return association.get_tcp_timeout().total_microseconds()/1000000.;
 }
 
-void set_tcp_timeout(odil::Association & association, float seconds)
+void set_tcp_timeout(odil::Association& association, float seconds)
 {
     association.set_tcp_timeout(
         boost::posix_time::microseconds(seconds*1000000.));
@@ -55,62 +59,64 @@ void wrap_Association()
 {
     using namespace boost::python;
     using namespace odil;
-    
+
     object wrapped_exception = scope().attr("Exception");
-    
+
     wrapped_AssociationReleased = exception_factory(
         "AssociationReleased", wrapped_exception.ptr());
     register_exception_translator<AssociationReleased>(released_translator);
-    
+
     wrapped_AssociationAborted = exception_factory(
         "AssociationAborted", wrapped_exception.ptr());
     register_exception_translator<AssociationAborted>(aborted_translator);
-    
+
     scope association_scope = class_<Association>("Association", init<>())
-        .def("get_peer_host", &Association::get_peer_host,
-            return_value_policy<copy_const_reference>())
-        .def("set_peer_host", &Association::set_peer_host)
-        .def("get_peer_port", &Association::get_peer_port)
-        .def("set_peer_port", &Association::set_peer_port)
-        .def(
-            "get_parameters", 
-            &Association::get_parameters, 
-            return_value_policy<reference_existing_object>()
+                              .def("get_peer_host", &Association::get_peer_host,
+                                   return_value_policy<copy_const_reference>())
+                              .def("set_peer_host", &Association::set_peer_host)
+                              .def("get_peer_port", &Association::get_peer_port)
+                              .def("set_peer_port", &Association::set_peer_port)
+                              .def(
+        "get_parameters",
+        &Association::get_parameters,
+        return_value_policy<reference_existing_object>()
         )
-        .def(
-            "set_parameters", 
-            &Association::set_parameters, 
-            return_value_policy<reference_existing_object>()
+                              .def(
+        "set_parameters",
+        &Association::set_parameters,
+        return_value_policy<reference_existing_object>()
         )
-        .def(
-            "update_parameters", 
-            &Association::update_parameters, 
-            return_value_policy<reference_existing_object>()
+                              .def(
+        "update_parameters",
+        &Association::update_parameters,
+        return_value_policy<reference_existing_object>()
         )
-        .def(
-            "get_negotiated_parameters", 
-            &Association::get_negotiated_parameters, 
-            return_value_policy<reference_existing_object>()
+                              .def(
+        "get_negotiated_parameters",
+        &Association::get_negotiated_parameters,
+        return_value_policy<reference_existing_object>()
         )
-        .def("get_tcp_timeout", &get_tcp_timeout)
-        .def("set_tcp_timeout", &set_tcp_timeout)
-        // Message timeout
-        .def("is_associated", &Association::is_associated)
-        .def("associate", &Association::associate)
-        // Receive association
-        .def("receive_association", &receive_association)
-        //.def("reject", &Association::reject)
-        .def("release", &Association::release)
-        .def("abort", &Association::abort)
-        // Receive message
-        .def("receive_message", &Association::receive_message)
-        // Send message
-        .def("next_message_id", &Association::next_message_id)
+                              .def("get_tcp_timeout", &get_tcp_timeout)
+                              .def("set_tcp_timeout", &set_tcp_timeout)
+                              // Message timeout
+                              .def("is_associated", &Association::is_associated)
+                              .def("associate", &Association::associate)
+                              // Receive association
+                              .def("receive_association", &receive_association)
+                              //.def("reject", &Association::reject)
+                              .def("release", &Association::release)
+                              .def("abort", &Association::abort)
+                              // Receive message
+                              .def("receive_message", &Association::receive_message)
+                              // Send message
+                              .def("next_message_id", &Association::next_message_id)
+                              // Send message
+                              .def("send_message", &Association::send_message)
     ;
 
     enum_<Association::Result>("Result")
-        .value("Accepted", Association::Accepted)
-        .value("RejectedPermanent", Association::RejectedPermanent)
-        .value("RejectedTransient", Association::RejectedTransient)
+    .value("Accepted", Association::Accepted)
+    .value("RejectedPermanent", Association::RejectedPermanent)
+    .value("RejectedTransient", Association::RejectedTransient)
     ;
 }
diff --git a/wrappers/AssocationParameters.cpp b/wrappers/AssocationParameters.cpp
index e799795..47dc2f0 100644
--- a/wrappers/AssocationParameters.cpp
+++ b/wrappers/AssocationParameters.cpp
@@ -209,7 +209,8 @@ void wrap_AssociationParameters()
 
     {
         scope user_identity_scope = 
-            class_<AssociationParameters::UserIdentity>("UserIdentity")
+            class_<AssociationParameters::UserIdentity>(
+                "UserIdentity", init<>())
             .def_readwrite(
                 "type", 
                 &AssociationParameters::UserIdentity::type
diff --git a/wrappers/CEchoResponse.cpp b/wrappers/CEchoResponse.cpp
new file mode 100644
index 0000000..107ac6f
--- /dev/null
+++ b/wrappers/CEchoResponse.cpp
@@ -0,0 +1,30 @@
+/*************************************************************************
+ * odil - Copyright (C) Universite de Strasbourg
+ * Distributed under the terms of the CeCILL-B license, as published by
+ * the CEA-CNRS-INRIA. Refer to the LICENSE file or to
+ * http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html
+ * for details.
+ ************************************************************************/
+
+#include <boost/python.hpp>
+
+#include "odil/message/CEchoResponse.h"
+
+void wrap_CEchoResponse()
+{
+    using namespace boost::python;
+    using namespace odil;
+    using namespace odil::message;
+
+    class_<CEchoResponse, bases<Response>>(
+            "CEchoResponse", init<Value::Integer,Value::Integer, Value::String const &>())
+        .def(init<Message const &>())
+        .def(
+            "get_affected_sop_class_uid",
+            &CEchoResponse::get_affected_sop_class_uid,
+            return_value_policy<copy_const_reference>())
+        .def(
+            "set_affected_sop_class_uid",
+            &CEchoResponse::set_affected_sop_class_uid)
+    ;
+}
diff --git a/wrappers/CMakeLists.txt b/wrappers/CMakeLists.txt
index d5ab3b8..3d4d7aa 100644
--- a/wrappers/CMakeLists.txt
+++ b/wrappers/CMakeLists.txt
@@ -1,13 +1,25 @@
 find_package(Boost REQUIRED COMPONENTS python)
 find_package(JsonCpp REQUIRED)
+find_package(PythonInterp 2.7 REQUIRED)
 find_package(PythonLibs 2.7 REQUIRED)
 
+if(NOT "${PYTHONLIBS_VERSION_STRING}" STREQUAL "${PYTHON_VERSION_STRING}")
+    message(
+        WARNING
+        "Python version mismatch:  libraries are \"${PYTHONLIBS_VERSION_STRING}\", interpreter is \"${PYTHON_VERSION_STRING}\"")
+endif()
+
+if(BUILD_SHARED_LIBS)
+    add_definitions(-D BOOST_ALL_DYN_LINK)
+endif()
+
 include_directories(
     ${CMAKE_CURRENT_SOURCE_DIR}/../src ${Boost_INCLUDE_DIRS}
     ${JsonCpp_INCLUDE_DIRS} ${PYTHON_INCLUDE_DIRS})
 link_directories(${Boost_LIBRARY_DIRS})
 
 file(GLOB_RECURSE files "*.cpp")
+list(SORT files)
 python_add_module(pyodil SHARED ${files})
 set_target_properties(
     pyodil PROPERTIES OUTPUT_NAME odil FOLDER "Wrappers")
@@ -17,3 +29,11 @@ endif()
 
 target_link_libraries(
     pyodil ${Boost_LIBRARIES} ${JsonCpp_LIBRARIES} libodil ${PYTHON_LIBRARIES})
+
+execute_process(
+    COMMAND ${PYTHON_EXECUTABLE}
+      -c "from distutils import sysconfig; print( sysconfig.get_python_lib( plat_specific=True, prefix='${CMAKE_INSTALL_PREFIX}' ) )"
+    OUTPUT_VARIABLE PYTHON_SITE_PACKAGES
+    OUTPUT_STRIP_TRAILING_WHITESPACE)
+
+install(TARGETS pyodil DESTINATION ${PYTHON_SITE_PACKAGES})
diff --git a/wrappers/CStoreResponse.cpp b/wrappers/CStoreResponse.cpp
new file mode 100644
index 0000000..fbd923b
--- /dev/null
+++ b/wrappers/CStoreResponse.cpp
@@ -0,0 +1,54 @@
+/*************************************************************************
+ * odil - Copyright (C) Universite de Strasbourg
+ * Distributed under the terms of the CeCILL-B license, as published by
+ * the CEA-CNRS-INRIA. Refer to the LICENSE file or to
+ * http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html
+ * for details.
+ ************************************************************************/
+
+#include <boost/python.hpp>
+
+#include "odil/message/CStoreResponse.h"
+
+void wrap_CStoreResponse()
+{
+    using namespace boost::python;
+    using namespace odil;
+    using namespace odil::message;
+
+    class_<CStoreResponse, bases<Response>>(
+                "CStoreResponse",
+                init< Value::Integer, Value::Integer>())
+            .def(init<Message>())
+            .def(
+                "has_message_id",
+                &CStoreResponse::has_message_id)
+            .def(
+                "get_message_id",
+                &CStoreResponse::get_message_id,
+                return_value_policy<copy_const_reference>())
+            .def(
+                "set_message_id",
+                &CStoreResponse::set_message_id)
+            .def(
+                "has_affected_sop_class_uid",
+                &CStoreResponse::has_affected_sop_class_uid)
+            .def(
+                "get_affected_sop_class_uid",
+                &CStoreResponse::get_affected_sop_class_uid,
+                return_value_policy<copy_const_reference>())
+            .def(
+                "set_affected_sop_class_uid",
+                &CStoreResponse::set_affected_sop_class_uid)
+            .def(
+                "has_affected_sop_instance_uid",
+                &CStoreResponse::has_affected_sop_instance_uid)
+            .def(
+                "get_affected_sop_instance_uid",
+                &CStoreResponse::get_affected_sop_instance_uid,
+                return_value_policy<copy_const_reference>())
+            .def(
+                "set_affected_sop_instance_uid",
+                &CStoreResponse::set_affected_sop_instance_uid)
+            ;
+}
diff --git a/wrappers/DataSet.cpp b/wrappers/DataSet.cpp
index e9cd25e..f15fb8b 100644
--- a/wrappers/DataSet.cpp
+++ b/wrappers/DataSet.cpp
@@ -247,5 +247,7 @@ void wrap_DataSet()
         .def(
             "__len__",
             static_cast<std::size_t (DataSet::*)() const>(&DataSet::size))
+        .def("clear", &DataSet::clear)
     ;
 }
+
diff --git a/wrappers/EchoSCP.cpp b/wrappers/EchoSCP.cpp
index e6ee791..edb6025 100644
--- a/wrappers/EchoSCP.cpp
+++ b/wrappers/EchoSCP.cpp
@@ -6,22 +6,28 @@
  * for details.
  ************************************************************************/
 
-#include <boost/python.hpp>
-
 #include "odil/EchoSCP.h"
 
+#include <boost/python.hpp>
+
 namespace
 {
 
-void 
-set_callback(odil::EchoSCP & scp, boost::python::object const & f)
+void
+set_callback(odil::EchoSCP& scp, boost::python::object const& f)
 {
     scp.set_callback(
-        [f](odil::message::CEchoRequest const & message) 
-        { 
+        [f](odil::message::CEchoRequest const& message)
+        {
             return boost::python::call<odil::Value::Integer>(f.ptr(), message);
         }
-    );
+        );
+}
+
+std::shared_ptr<odil::EchoSCP>
+New_EchoSCP( odil::Association& a )
+{
+    return std::shared_ptr<odil::EchoSCP>( new odil::EchoSCP(a) );
 }
 
 }
@@ -31,8 +37,16 @@ void wrap_EchoSCP()
     using namespace boost::python;
     using namespace odil;
 
-    class_<EchoSCP>("EchoSCP", init<Association &>())
+    class_<EchoSCP>("EchoSCP", init<Association&>())
         .def("set_callback", &set_callback)
-        .def("__call__", &EchoSCP::operator())
+        .def(
+            "__call__",
+            static_cast<
+                void (EchoSCP::*)(message::Message const &)
+            >(&EchoSCP::operator())
+        )
     ;
+
+
+    def("New_EchoSCP", &New_EchoSCP);
 }
diff --git a/wrappers/Element.cpp b/wrappers/Element.cpp
index bcf24ec..7c251d3 100644
--- a/wrappers/Element.cpp
+++ b/wrappers/Element.cpp
@@ -18,11 +18,9 @@ namespace
 {
 
 boost::shared_ptr<odil::Element>
-constructor(
-    boost::python::object const & value_python, odil::VR vr=odil::VR::INVALID)
+constructor(boost::python::object const & value, odil::VR vr)
 {
-    auto value_cpp = value_constructor(value_python);
-    
+    auto value_cpp = value_constructor(value);
     odil::Element * element = new odil::Element(*value_cpp, vr);
     
     // Old versions of Boost.Python (Debian 7, Ubuntu 12.04) do not like 
@@ -43,9 +41,10 @@ void wrap_Element()
     typedef Value::DataSets & (Element::*AsDataSets)();
     typedef Value::Binary & (Element::*AsBinary)();
 
-    class_<Element>("Element", init<>())
+    class_<Element>("Element", no_init)
         .def_readwrite("vr", &Element::vr)
-        .def("__init__", make_constructor(constructor))
+        .def(init<VR>())
+        .def("__init__", make_constructor(&constructor))
         .def("empty", &Element::empty)
         .def("size", &Element::size)
         .def(
@@ -74,5 +73,6 @@ void wrap_Element()
         .def(self == self)
         .def(self != self)
         .def("__len__", &Element::size)
+        .def("clear", &Element::clear)
     ;
 }
diff --git a/wrappers/FindSCP.cpp b/wrappers/FindSCP.cpp
index 6a5809c..60ce40a 100644
--- a/wrappers/FindSCP.cpp
+++ b/wrappers/FindSCP.cpp
@@ -38,7 +38,12 @@ void wrap_FindSCP()
     
     scope find_scp_scope = class_<FindSCP>("FindSCP", init<Association &>())
         .def("set_generator", &set_generator)
-        .def("__call__", &FindSCP::operator())
+        .def(
+            "__call__",
+            static_cast<
+                void (FindSCP::*)(message::Message const &)
+            >(&FindSCP::operator())
+        )
     ;
     
     class_<
diff --git a/wrappers/GetSCP.cpp b/wrappers/GetSCP.cpp
index 26a8cb2..f983e22 100644
--- a/wrappers/GetSCP.cpp
+++ b/wrappers/GetSCP.cpp
@@ -55,7 +55,12 @@ void wrap_GetSCP()
     
     scope get_scp_scope = class_<GetSCP>("GetSCP", init<Association &>())
         .def("set_generator", &set_generator)
-        .def("__call__", &odil::GetSCP::operator())
+        .def(
+            "__call__",
+            static_cast<
+                void (GetSCP::*)(message::Message const &)
+            >(&GetSCP::operator())
+        )
     ;
     
     class_<DataSetGeneratorWrapperGet, boost::noncopyable>(
diff --git a/wrappers/Message.cpp b/wrappers/Message.cpp
index 655069f..fd370e5 100644
--- a/wrappers/Message.cpp
+++ b/wrappers/Message.cpp
@@ -6,11 +6,11 @@
  * for details.
  ************************************************************************/
 
-#include <boost/python.hpp>
-
 #include "odil/DataSet.h"
 #include "odil/message/Message.h"
 
+#include <boost/python.hpp>
+
 void wrap_Message()
 {
     using namespace boost::python;
@@ -18,18 +18,53 @@ void wrap_Message()
     using namespace odil::message;
 
     class_<Message>("Message", init<>())
-        .def(init<DataSet const &>())
-        .def(init<DataSet const &, DataSet const &>())
-        .def(
-            "get_command_set", &Message::get_command_set,
-            return_value_policy<copy_const_reference>())
-        .def("has_data_set", &Message::has_data_set)
-        .def(
-            "get_data_set", &Message::get_data_set,
-            return_value_policy<copy_const_reference>())
-        .def(
-            "get_command_field", &Message::get_command_field,
-            return_value_policy<copy_const_reference>())
-        .def("set_command_field", &Message::set_command_field)
+    .def(init<DataSet const&>())
+    .def(init<DataSet const&, DataSet const&>())
+    .def(
+        "get_command_set", &Message::get_command_set,
+        return_value_policy<copy_const_reference>())
+    .def("has_data_set", &Message::has_data_set)
+    .def(
+        "get_data_set",
+        static_cast<DataSet const & (Message::*)() const>(&Message::get_data_set),
+        return_value_policy<copy_const_reference>())
+    .def(
+        "get_command_field", &Message::get_command_field,
+        return_value_policy<copy_const_reference>())
+    .def("set_command_field", &Message::set_command_field)
     ;
 }
+
+void wrap_CommandTypeEnum()
+{
+    using namespace boost::python;
+    using namespace odil;
+    using namespace odil::message;
+
+    enum_< Message::Command::Type>("message_command_type")
+    .value("C_STORE_RQ", Message::Command::Type::C_STORE_RQ    )
+    .value("C_STORE_RSP", Message::Command::Type::C_STORE_RSP   )
+    .value("C_FIND_RQ", Message::Command::Type::C_FIND_RQ     )
+    .value("C_FIND_RSP", Message::Command::Type::C_FIND_RSP    )
+    .value("C_CANCEL_RQ", Message::Command::Type::C_CANCEL_RQ   )
+    .value("C_GET_RQ", Message::Command::Type::C_GET_RQ      )
+    .value("C_GET_RSP", Message::Command::Type::C_GET_RSP     )
+    .value("C_MOVE_RQ", Message::Command::Type::C_MOVE_RQ     )
+    .value("C_MOVE_RSP", Message::Command::Type::C_MOVE_RSP    )
+    .value("C_ECHO_RQ", Message::Command::Type::C_ECHO_RQ     )
+    .value("C_ECHO_RSP", Message::Command::Type::C_ECHO_RSP    )
+    .value("N_EVENT_REPORT_RQ", Message::Command::Type::N_EVENT_REPORT_RQ )
+    .value("N_EVENT_REPORT_RSP", Message::Command::Type::N_EVENT_REPORT_RSP)
+    .value("N_GET_RQ", Message::Command::Type::N_GET_RQ      )
+    .value("N_GET_RSP", Message::Command::Type::N_GET_RSP     )
+    .value("N_SET_RQ", Message::Command::Type::N_SET_RQ      )
+    .value("N_SET_RSP", Message::Command::Type::N_SET_RSP     )
+    .value("N_ACTION_RQ", Message::Command::Type::N_ACTION_RQ   )
+    .value("N_ACTION_RSP", Message::Command::Type::N_ACTION_RSP  )
+    .value("N_CREATE_RQ", Message::Command::Type::N_CREATE_RQ   )
+    .value("N_CREATE_RSP", Message::Command::Type::N_CREATE_RSP  )
+    .value("N_DELETE_RQ", Message::Command::Type::N_DELETE_RQ   )
+    .value("N_DELETE_RSP", Message::Command::Type::N_DELETE_RSP  )
+    ;
+}
+
diff --git a/wrappers/MoveSCP.cpp b/wrappers/MoveSCP.cpp
index e51bbab..24fc523 100644
--- a/wrappers/MoveSCP.cpp
+++ b/wrappers/MoveSCP.cpp
@@ -62,7 +62,12 @@ void wrap_MoveSCP()
     
     scope move_scp_scope = class_<MoveSCP>("MoveSCP", init<Association &>())
         .def("set_generator", &set_generator)
-        .def("__call__", &odil::MoveSCP::operator())
+        .def(
+            "__call__",
+            static_cast<
+                void (MoveSCP::*)(message::Message const &)
+            >(&MoveSCP::operator())
+        )
     ;
     
     class_<DataSetGeneratorWrapperMove, boost::noncopyable>(
diff --git a/wrappers/EchoSCP.cpp b/wrappers/NCreateSCP.cpp
similarity index 62%
copy from wrappers/EchoSCP.cpp
copy to wrappers/NCreateSCP.cpp
index e6ee791..52ad76c 100644
--- a/wrappers/EchoSCP.cpp
+++ b/wrappers/NCreateSCP.cpp
@@ -8,16 +8,16 @@
 
 #include <boost/python.hpp>
 
-#include "odil/EchoSCP.h"
+#include "odil/NCreateSCP.h"
 
 namespace
 {
 
 void 
-set_callback(odil::EchoSCP & scp, boost::python::object const & f)
+set_callback(odil::NCreateSCP & scp, boost::python::object const & f)
 {
     scp.set_callback(
-        [f](odil::message::CEchoRequest const & message) 
+        [f](odil::message::NCreateRequest const & message) 
         { 
             return boost::python::call<odil::Value::Integer>(f.ptr(), message);
         }
@@ -26,13 +26,18 @@ set_callback(odil::EchoSCP & scp, boost::python::object const & f)
 
 }
 
-void wrap_EchoSCP()
+void wrap_NCreateSCP()
 {
     using namespace boost::python;
     using namespace odil;
 
-    class_<EchoSCP>("EchoSCP", init<Association &>())
+    class_<NCreateSCP>("NCreateSCP", init<Association &>())
         .def("set_callback", &set_callback)
-        .def("__call__", &EchoSCP::operator())
+        .def(
+            "__call__",
+            static_cast<
+                void (NCreateSCP::*)(message::Message const &)
+            >(&NCreateSCP::operator())
+        )
     ;
 }
diff --git a/wrappers/NSetRequest.cpp b/wrappers/NSetRequest.cpp
new file mode 100644
index 0000000..4f7f05b
--- /dev/null
+++ b/wrappers/NSetRequest.cpp
@@ -0,0 +1,50 @@
+/*************************************************************************
+ * odil - Copyright (C) Universite de Strasbourg
+ * Distributed under the terms of the CeCILL-B license, as published by
+ * the CEA-CNRS-INRIA. Refer to the LICENSE file or to
+ * http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html
+ * for details.
+ ************************************************************************/
+
+#include <boost/python.hpp>
+
+#include "odil/message/NSetRequest.h"
+
+void wrap_NSetRequest()
+{
+    using namespace boost::python;
+    using namespace odil;
+    using namespace odil::message;
+
+    class_<NSetRequest, bases<Request>>(
+            "NSetRequest",
+            init<
+                Value::Integer, Value::String const &, Value::String const &,
+                DataSet const &
+            >())
+        .def(init<Message>())
+        .def(
+            "get_requested_sop_class_uid",
+            &NSetRequest::get_requested_sop_class_uid,
+            return_value_policy<copy_const_reference>())
+        .def(
+            "set_requested_sop_class_uid",
+            &NSetRequest::set_requested_sop_class_uid)
+        .def(
+            "get_requested_sop_instance_uid",
+            &NSetRequest::get_requested_sop_instance_uid,
+            return_value_policy<copy_const_reference>())
+        .def(
+            "set_requested_sop_instance_uid",
+            &NSetRequest::set_requested_sop_instance_uid)
+        .def("has_command_field",
+             &NSetRequest::set_command_field)
+        .def(
+            "get_command_field",
+            &NSetRequest::get_command_field,
+            return_value_policy<copy_const_reference>())
+        .def(
+            "set_command_field",
+            &NSetRequest::set_command_field)
+    ;
+}
diff --git a/wrappers/EchoSCP.cpp b/wrappers/NSetSCP.cpp
similarity index 59%
copy from wrappers/EchoSCP.cpp
copy to wrappers/NSetSCP.cpp
index e6ee791..afcdfff 100644
--- a/wrappers/EchoSCP.cpp
+++ b/wrappers/NSetSCP.cpp
@@ -6,33 +6,38 @@
  * for details.
  ************************************************************************/
 
-#include <boost/python.hpp>
+#include "odil/NSetSCP.h"
 
-#include "odil/EchoSCP.h"
+#include <boost/python.hpp>
 
 namespace
 {
 
-void 
-set_callback(odil::EchoSCP & scp, boost::python::object const & f)
+void
+set_callback( odil::NSetSCP& scp, boost::python::object const& f)
 {
     scp.set_callback(
-        [f](odil::message::CEchoRequest const & message) 
-        { 
+        [f](odil::message::NSetRequest const& message)
+        {
             return boost::python::call<odil::Value::Integer>(f.ptr(), message);
         }
-    );
+        );
 }
-
 }
 
-void wrap_EchoSCP()
+void wrap_NSetSCP()
 {
     using namespace boost::python;
     using namespace odil;
 
-    class_<EchoSCP>("EchoSCP", init<Association &>())
+    class_<NSetSCP >("NSetSCP", init<Association&>() )
+        .def (init<Association&, NSetSCP::Callback&>())
         .def("set_callback", &set_callback)
-        .def("__call__", &EchoSCP::operator())
+        .def(
+            "__call__",
+            static_cast<
+                void (NSetSCP::*)(message::Message const &)
+            >(&NSetSCP::operator())
+        )
     ;
 }
diff --git a/wrappers/StoreSCU.cpp b/wrappers/NSetSCU.cpp
similarity index 63%
copy from wrappers/StoreSCU.cpp
copy to wrappers/NSetSCU.cpp
index 1481201..9d8678f 100644
--- a/wrappers/StoreSCU.cpp
+++ b/wrappers/NSetSCU.cpp
@@ -8,29 +8,27 @@
 
 #include <boost/python.hpp>
 
-#include "odil/StoreSCU.h"
+#include "odil/NSetSCU.h"
 
-BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(storeMethod, odil::StoreSCU::store, 1, 3)
 
-void wrap_StoreSCU()
+void wrap_NSetSCU()
 {
     using namespace boost::python;
     using namespace odil;
 
-    class_<StoreSCU>("StoreSCU", init<Association &>())
+    class_<NSetSCU>("NSetSCU", init<Association &>())
         .def(
             "get_affected_sop_class",
-            &StoreSCU::get_affected_sop_class,
+            &NSetSCU::get_affected_sop_class,
             return_value_policy<copy_const_reference>()
         )
         .def(
             "set_affected_sop_class",
-            static_cast<void(StoreSCU::*)(DataSet const &)>(&StoreSCU::set_affected_sop_class)
+            static_cast<void(NSetSCU::*)(DataSet const &)>(&NSetSCU::set_affected_sop_class)
         )
         .def(
-            "store", 
-            &StoreSCU::store,
-            storeMethod()
+            "set",
+            &NSetSCU::set
         )
     ;
 }
diff --git a/wrappers/Response.cpp b/wrappers/Response.cpp
index 492c57e..7099c03 100644
--- a/wrappers/Response.cpp
+++ b/wrappers/Response.cpp
@@ -41,3 +41,37 @@ void wrap_Response()
             static_cast<bool (Response::*)() const>(&Response::is_failure))
     ;
 }
+
+void wrap_ResponseStatus()
+{
+    using namespace boost::python;
+    using namespace odil;
+    using namespace odil::message;
+
+    enum_< Response::Status >("response_status")
+            .value("Success", Response::Status::Success )
+            .value("Cancel", Response::Status::Cancel )
+            .value("Pending", Response::Status::Pending )
+            .value("AttributeListError", Response::Status::AttributeListError )
+            .value("AttributeValueOutOfRange", Response::Status::AttributeValueOutOfRange )
+            .value("SOPClassNotSupported", Response::Status::SOPClassNotSupported )
+            .value("ClassInstanceConflict", Response::Status::ClassInstanceConflict )
+            .value("DuplicateSOPInstance", Response::Status::DuplicateSOPInstance )
+            .value("DuplicateInvocation", Response::Status::DuplicateInvocation )
+            .value("InvalidArgumentValue", Response::Status::InvalidArgumentValue )
+            .value("InvalidAttributeValue", Response::Status::InvalidAttributeValue )
+            .value("InvalidObjectInstance", Response::Status::InvalidObjectInstance )
+            .value("MissingAttribute", Response::Status::MissingAttribute )
+            .value("MissingAttributeValue", Response::Status::MissingAttributeValue )
+            .value("MistypedArgument", Response::Status::MistypedArgument )
+            .value("NoSuchArgument", Response::Status::NoSuchArgument )
+            .value("NoSuchAttribute", Response::Status::NoSuchAttribute )
+            .value("NoSuchEventType", Response::Status::NoSuchEventType )
+            .value("NoSuchSOPInstance", Response::Status::NoSuchSOPInstance )
+            .value("NoSuchSOPClass", Response::Status::NoSuchSOPClass )
+            .value("ProcessingFailure", Response::Status::ProcessingFailure )
+            .value("ResourceLimitation", Response::Status::ResourceLimitation )
+            .value("UnrecognizedOperation", Response::Status::UnrecognizedOperation )
+            .value("NoSuchActionType", Response::Status::NoSuchActionType)
+    ;
+}
diff --git a/wrappers/EchoSCP.cpp b/wrappers/SCP.cpp
similarity index 73%
copy from wrappers/EchoSCP.cpp
copy to wrappers/SCP.cpp
index e6ee791..116e50e 100644
--- a/wrappers/EchoSCP.cpp
+++ b/wrappers/SCP.cpp
@@ -8,13 +8,13 @@
 
 #include <boost/python.hpp>
 
-#include "odil/EchoSCP.h"
-
+#include "odil/SCP.h"
+/*
 namespace
 {
 
 void 
-set_callback(odil::EchoSCP & scp, boost::python::object const & f)
+set_callback(odil::SCP & scp, boost::python::object const & f)
 {
     scp.set_callback(
         [f](odil::message::CEchoRequest const & message) 
@@ -25,14 +25,15 @@ set_callback(odil::EchoSCP & scp, boost::python::object const & f)
 }
 
 }
+*/
 
-void wrap_EchoSCP()
+void wrap_SCP()
 {
     using namespace boost::python;
     using namespace odil;
 
-    class_<EchoSCP>("EchoSCP", init<Association &>())
-        .def("set_callback", &set_callback)
-        .def("__call__", &EchoSCP::operator())
+    class_<SCP, bases<>, boost::shared_ptr<SCP>, boost::noncopyable>("SCP", no_init)
+        .def("receive_and_process", &SCP::receive_and_process)
     ;
+
 }
diff --git a/wrappers/SCPDispatcher.cpp b/wrappers/SCPDispatcher.cpp
new file mode 100644
index 0000000..b4276fb
--- /dev/null
+++ b/wrappers/SCPDispatcher.cpp
@@ -0,0 +1,47 @@
+/*************************************************************************
+ * odil - Copyright (C) Universite de Strasbourg
+ * Distributed under the terms of the CeCILL-B license, as published by
+ * the CEA-CNRS-INRIA. Refer to the LICENSE file or to
+ * http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html
+ * for details.
+ ************************************************************************/
+
+#include "odil/EchoSCP.h"
+#include "odil/NSetSCP.h"
+#include "odil/SCP.h"
+#include "odil/SCPDispatcher.h"
+#include "odil/StoreSCP.h"
+
+#include <boost/python.hpp>
+
+namespace
+{
+
+void dispatch_in_python( odil::SCPDispatcher& dispatcher)
+{
+    dispatcher.dispatch();
+}
+
+template<typename TSCP, odil::message::Message::Command::Type Command>
+void set_scp(odil::SCPDispatcher & dispatcher, TSCP scp)
+{
+    dispatcher.set_scp(Command, std::make_shared<TSCP>( scp ));
+}
+
+}
+
+void wrap_SCPDispatcher()
+{
+    using namespace boost::python;
+    using namespace odil;
+
+    class_<SCPDispatcher >("SCPDispatcher", init<Association&>())
+    .def("set_scp", &SCPDispatcher::set_scp )
+    .def("dispatch", &dispatch_in_python )
+    .def("set_echo_scp", &set_scp<EchoSCP, message::Message::Command::Type::C_ECHO_RQ>)
+    .def("set_store_scp", &set_scp<StoreSCP, message::Message::Command::Type::C_STORE_RQ>)
+    .def("set_nset_scp", &set_scp<NSetSCP, message::Message::Command::Type::N_SET_RQ>)
+    ;
+}
+
+
diff --git a/wrappers/StoreSCP.cpp b/wrappers/StoreSCP.cpp
index 3e6d0af..49ed7ed 100644
--- a/wrappers/StoreSCP.cpp
+++ b/wrappers/StoreSCP.cpp
@@ -6,22 +6,24 @@
  * for details.
  ************************************************************************/
 
+#include "odil/StoreSCP.h"
+
 #include <boost/python.hpp>
 
-#include "odil/StoreSCP.h"
+#include <functional>
 
 namespace
 {
-
-void 
-set_callback(odil::StoreSCP & scp, boost::python::object const & f)
+using namespace std;
+void
+set_callback(odil::StoreSCP& scp, boost::python::object const& f)
 {
     scp.set_callback(
-        [f](odil::message::CStoreRequest const & message) 
-        { 
+        [f](odil::message::CStoreRequest const& message)
+        {
             return boost::python::call<odil::Value::Integer>(f.ptr(), message);
         }
-    );
+        );
 }
 
 }
@@ -29,10 +31,19 @@ set_callback(odil::StoreSCP & scp, boost::python::object const & f)
 void wrap_StoreSCP()
 {
     using namespace boost::python;
+    using namespace std;
     using namespace odil;
 
-    class_<StoreSCP>("StoreSCP", init<Association &>())
-        .def("set_callback", &set_callback)
-        .def("__call__", &StoreSCP::operator())
+//    class_<StoreSCP, bases<SCP>>("StoreSCP", init<Association &>())
+    class_<StoreSCP>("StoreSCP", init<Association&>())
+    .def(init<Association&, StoreSCP::Callback& >())
+    .def("set_callback", &set_callback)
+    .def(
+        "__call__",
+        static_cast<
+            void (StoreSCP::*)(message::Message const &)
+        >(&StoreSCP::operator())
+    )
     ;
+
 }
diff --git a/wrappers/StoreSCU.cpp b/wrappers/StoreSCU.cpp
index 1481201..ca7704f 100644
--- a/wrappers/StoreSCU.cpp
+++ b/wrappers/StoreSCU.cpp
@@ -10,13 +10,18 @@
 
 #include "odil/StoreSCU.h"
 
-BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(storeMethod, odil::StoreSCU::store, 1, 3)
+BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(
+    store_overloads, odil::StoreSCU::store, 1, 3)
 
 void wrap_StoreSCU()
 {
     using namespace boost::python;
     using namespace odil;
 
+    typedef
+        void (StoreSCU::*StoreFunction)(
+            DataSet const &, Value::String const &, Value::Integer) const;
+
     class_<StoreSCU>("StoreSCU", init<Association &>())
         .def(
             "get_affected_sop_class",
@@ -28,9 +33,8 @@ void wrap_StoreSCU()
             static_cast<void(StoreSCU::*)(DataSet const &)>(&StoreSCU::set_affected_sop_class)
         )
         .def(
-            "store", 
-            &StoreSCU::store,
-            storeMethod()
-        )
+            "store",
+            static_cast<StoreFunction>(&odil::StoreSCU::store),
+            store_overloads())
     ;
 }
diff --git a/wrappers/Tag.cpp b/wrappers/Tag.cpp
index e201419..26bf353 100644
--- a/wrappers/Tag.cpp
+++ b/wrappers/Tag.cpp
@@ -10,6 +10,11 @@
 
 #include "odil/Tag.h"
 
+uint32_t hash(odil::Tag const & tag)
+{
+    return ((tag.group<<16)+tag.element);
+}
+
 void wrap_Tag()
 {
     using namespace boost::python;
@@ -29,6 +34,7 @@ void wrap_Tag()
         .def(self <= self)
         .def(self >= self)
         .def("__str__", &Tag::operator std::string)
+        .def("__hash__", &hash)
     ;
     implicitly_convertible<std::string, Tag>();
 }
diff --git a/wrappers/VR.cpp b/wrappers/VR.cpp
index 1dcc4a6..ba57161 100644
--- a/wrappers/VR.cpp
+++ b/wrappers/VR.cpp
@@ -8,8 +8,72 @@
 
 #include <boost/python.hpp>
 
+#include "odil/Exception.h"
 #include "odil/VR.h"
 
+#if PY_MAJOR_VERSION >= 3
+    #define AsString(x) PyBytes_AsString(x)
+#else
+    #define IS_PY2
+    #define AsString(x) PyString_AsString(x)
+#endif
+
+odil::VR as_vr(PyObject * object)
+{
+    PyObject * utf8 = nullptr;
+    if(PyUnicode_Check(object))
+    {
+        // New reference
+        utf8 = PyUnicode_AsUTF8String(object);
+    }
+#ifdef IS_PY2
+    else if(PyString_Check(object))
+    {
+        utf8 = object;
+        // Increase reference count to match PyUnicode_AsUTF8String
+        Py_INCREF(utf8);
+    }
+#endif
+
+    if(utf8 == nullptr)
+    {
+        throw odil::Exception("Object is not string-like");
+    }
+
+    std::string const vr_name(AsString(utf8));
+    Py_DECREF(utf8);
+
+    return odil::as_vr(vr_name);
+}
+
+void * convertible(PyObject* object)
+{
+    bool result=true;
+    try
+    {
+        as_vr(object);
+    }
+    catch(...)
+    {
+        result = false;
+    }
+
+    return result?object:nullptr;
+}
+
+void construct(
+    PyObject* object,
+    boost::python::converter::rvalue_from_python_stage1_data* data)
+{
+    auto const vr = as_vr(object);
+
+    void * storage = reinterpret_cast<
+            boost::python::converter::rvalue_from_python_storage<odil::VR>*
+        >(data)->storage.bytes;
+    new (storage) odil::VR(vr);
+    data->convertible = storage;
+}
+
 void wrap_VR()
 {
     using namespace boost::python;
@@ -48,5 +112,11 @@ void wrap_VR()
         .value("UT", VR::UT)
         .value("INVALID", VR::INVALID)
     ;
+    converter::registry::push_back(
+        &convertible, &construct, type_id<VR>());
+    def("is_int", odil::is_int);
+    def("is_real", odil::is_real);
+    def("is_string", odil::is_string);
+    def("is_binary", odil::is_binary);
 }
 
diff --git a/wrappers/Value.cpp b/wrappers/Value.cpp
index 829b13e..e9ab1ba 100644
--- a/wrappers/Value.cpp
+++ b/wrappers/Value.cpp
@@ -62,10 +62,11 @@ void wrap_Value()
     typedef Value::Binary & (Value::*AsBinary)();
 
     // Define scope to enclose Integers, Reals, etc. in Value
-    scope value_scope = class_<Value>("Value", init<>())
+    scope value_scope = class_<Value>("Value", no_init)
         .def("__init__", make_constructor(value_constructor))
         .add_property("type", &Value::get_type)
         .def("empty", &Value::empty)
+        .def("size", &Value::size)
         .def(
             "as_integers", AsIntegers(&Value::as_integers), 
             return_value_policy<reference_existing_object>())
@@ -83,10 +84,11 @@ void wrap_Value()
             return_value_policy<reference_existing_object>())
         .def(self == self)
         .def(self != self)
+        .def("clear", &Value::clear)
+        .def("__len__", &Value::size)
     ;
     
     enum_<Value::Type>("Type")
-        .value("Empty", Value::Type::Empty)
         .value("Integers", Value::Type::Integers)
         .value("Reals", Value::Type::Reals)
         .value("Strings", Value::Type::Strings)
diff --git a/wrappers/odil.cpp b/wrappers/odil.cpp
index b2890e6..86dbdf2 100644
--- a/wrappers/odil.cpp
+++ b/wrappers/odil.cpp
@@ -1,4 +1,4 @@
-/*************************************************************************
+    /*************************************************************************
  * odil - Copyright (C) Universite de Strasbourg
  * Distributed under the terms of the CeCILL-B license, as published by
  * the CEA-CNRS-INRIA. Refer to the LICENSE file or to
@@ -24,7 +24,12 @@ void wrap_GetSCU();
 void wrap_json_converter();
 void wrap_MoveSCP();
 void wrap_MoveSCU();
+void wrap_NCreateSCP();
+void wrap_NSetSCP();
+void wrap_NSetSCU();
 void wrap_read();
+//__attribute__((__visibility__("default")))
+void wrap_SCPDispatcher();
 void wrap_StoreSCU();
 void wrap_StoreSCP();
 void wrap_Tag();
@@ -39,6 +44,8 @@ void wrap_xml_converter();
 void wrap_registry();
 
 void wrap_Message();
+void wrap_CommandTypeEnum();
+void wrap_ResponseStatus();
 void wrap_Request();
 void wrap_Response();
 void wrap_CEchoRequest();
@@ -48,6 +55,8 @@ void wrap_CGetResponse();
 void wrap_CMoveRequest();
 void wrap_CMoveResponse();
 void wrap_CStoreRequest();
+void wrap_CStoreResponse();
+void wrap_NSetRequest();
 
 BOOST_PYTHON_MODULE(odil)
 {
@@ -68,7 +77,11 @@ BOOST_PYTHON_MODULE(odil)
     wrap_json_converter();
     wrap_MoveSCP();
     wrap_MoveSCU();
+    wrap_NCreateSCP();
+    wrap_NSetSCP();
+    wrap_NSetSCU();
     wrap_read();
+    wrap_SCPDispatcher();
     wrap_StoreSCP();
     wrap_StoreSCU();
     wrap_Tag();
@@ -83,6 +96,8 @@ BOOST_PYTHON_MODULE(odil)
     wrap_registry();
 
     wrap_Message();
+    wrap_CommandTypeEnum();
+    wrap_ResponseStatus();
     wrap_Request();
     wrap_Response();
     wrap_CEchoRequest();
@@ -92,4 +107,6 @@ BOOST_PYTHON_MODULE(odil)
     wrap_CMoveRequest();
     wrap_CMoveResponse();
     wrap_CStoreRequest();
+    wrap_CStoreResponse();
+    wrap_NSetRequest();
 }
diff --git a/wrappers/value_constructor.cpp b/wrappers/value_constructor.cpp
index 0206074..77fcbaf 100644
--- a/wrappers/value_constructor.cpp
+++ b/wrappers/value_constructor.cpp
@@ -14,20 +14,51 @@
 #include "odil/DataSet.h"
 #include "odil/Value.h"
 
+#if PY_MAJOR_VERSION >= 3
+#define IS_PY3K
+#endif
+
 boost::shared_ptr<odil::Value>
 value_constructor(boost::python::object const & source)
 {
     odil::Value * result = nullptr;
     if(boost::python::len(source) == 0)
     {
-        result = new odil::Value();
+        if(boost::python::extract<odil::Value::Integers>(source).check())
+        {
+            result = new odil::Value(odil::Value::Integers());
+        }
+        else if(boost::python::extract<odil::Value::Reals>(source).check())
+        {
+            result = new odil::Value(odil::Value::Reals());
+        }
+        else if(boost::python::extract<odil::Value::Strings>(source).check())
+        {
+            result = new odil::Value(odil::Value::Strings());
+        }
+        else if(boost::python::extract<odil::Value::DataSets>(source).check())
+        {
+            result = new odil::Value(odil::Value::DataSets());
+        }
+        else if(boost::python::extract<odil::Value::Binary>(source).check())
+        {
+            result = new odil::Value(odil::Value::Binary());
+        }
+        else
+        {
+            throw odil::Exception("Unknown empty type");
+        }
     }
     else
     {
         boost::python::object first = source[0];
         PyObject * first_ptr = first.ptr();
         
+#ifdef IS_PY3K
+        if(PyLong_Check(first_ptr))
+#else
         if(PyInt_Check(first_ptr))
+#endif
         {
             boost::python::stl_input_iterator<odil::Value::Integer> 
                 begin(source), end;
@@ -39,7 +70,11 @@ value_constructor(boost::python::object const & source)
                 begin(source), end;
             result = new odil::Value(odil::Value::Reals(begin, end));
         }
+#ifdef IS_PY3K
+        else if(PyUnicode_Check(first_ptr))
+#else
         else if(PyString_Check(first_ptr))
+#endif
         {
             boost::python::stl_input_iterator<odil::Value::String> 
                 begin(source), end;
@@ -72,6 +107,10 @@ value_constructor(boost::python::object const & source)
                     begin(source), end;
                 result = new odil::Value(odil::Value::DataSets(begin, end));
             }
+            else
+            {
+                throw odil::Exception("Unknown value type");
+            }
         }
     }
     

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/debian-med/odil.git



More information about the debian-med-commit mailing list