Skip to content

Commit

Permalink
Add data read (#85)
Browse files Browse the repository at this point in the history
* Define base classes for reading
* Support reading datasets and attributes
* Add functions to construct ReadDatasetWrapper and ReadAttributeWrapper from BaseIO
* Add test for using the ReadDatasetWrapper
* Read ElectricalSeries.data example working
* Add example for data read
* Add user docs for data read
* Update read user docs
* Add read software design figure and more details on the design
* Implement function to allow us to get the storage object type for a given path
* Rename getObjectType to getStroageObjectType
* Fix reading of attributes and add TimeSeries.resolution read
* Add example for reading an attribute
* Add ReadDataWrapperBase as common base class for ReadDatasetWrapper and ReadAttributeWrapper
* Remove NWBFile.identifierText property
* Allow setting of value type template parameter for ReadDataWrapperBase and subtypes
* Save the std::type_index as part of the DataBlock to support runtime checking
* Fix #88 move io classes to their own module
* Refactor to add IO namespace to match folder structure
* Move HDF5RecordingData to its own hpp/cpp files
* Moved read I/O classes to new ReadIO.hpp
* Refactor the read data wrappers to use a single ReadDataWrapper class for both Attributes and Datasets
* Replace redundant ReadObjectType with StorageObjectType
* Add traits for ReadObjectWrapper to prevent instantiation for Group and Undefined types
* Update ElectrodGroup to provide standard Container constructor and move args to initalize
* Register subtypes
* Move the registry to a new RegisteredType class to also cover non-Group types, e.g., Data, VectorData etc.
* Ensure all neurodata_types are registered correctly
* Add unittest for RegisteredTrype
* Add developer docs for RegisteredTypes
* Update read test to use the registry to construct the class
* Fix getTypeName and getNamespace and use them instead of hard-coded values
* Add test for reading a TimeSeries Container back
* Add RegisteredType::create method to read a type from file
* Run test workflows on all PRs

* Fix static assert for ReadDataWrapper

* Sync add_read with main branch (#101)

* Merged main into add_read

* Fix docs build for SpikeEventSeries

* Fix code formatting

* Fix segfault due to duplicate declaration of NWBFile.io parameter

* Fix formatting and docs error after merge with main

* Fix formatting after merge

* Remove SpikeEventSeries.neurodata_type

* Register SpikeEventSeries as a type

* Updated RegisterTypes to allow overwrite of the typename to use and overwrite the typename for ElectrodeTable

* Fix code formatting

* Add missing description for DynamicTableRegion

* Fix #109 Add missing axis attribute for channel_conversion and remove extra attributes

* Remove undefined functions from Device

* Rename member variable in ElectricalSeries

* Rename member variable in SpikeEventSeries

* Added mergePaths functions and made functions in Utils.hpp static inline

* Updated ElectrodeTable to use mergePaths method

* Updated DynamicTable to use mergePaths method

* Update ElectrodeGroup to use mergePaths

* Updated ElectricalSeries to use mergePaths

* Updated TimerSeries to use mergePaths function

* Updated NWBFile to use the mergePaths function

* Updated ElectrodeTable static paths to not used trailing / for consistency

* Fix Doxygen error by extracting also static members in the docs

* Fix section level in install.dox

* Update ReadDataWrapper to use m_ member naming convention

* Updated DataBlock / DataBlockGeneric to avoid shadowing of constructor args

* Fix reference error in read.dox

* Add ReadDataWrapper isType, getPath, getIO methods and update docstrings

* Create macro DEFINE_FIELD to simplify creating access methods for fields in RegisteredType subclasses

* Replace previous field access methods in TimeSeries with DEFINE_FIELD macro calls

* Replace TimeSeries.dataLazy and resolutionLazy with DEFINE_FIELD definitions

* Update the Doxygen built to expand the DEFINE_FIELD macro to document the generated functions

* Fix spellcheck

* Fix section levels in read.dox

* Add docs for using the DEFINE_FIELD macro

* Added attributeExists method on IO and ReadDataWrapper::exists methods to check that an object actually exists

* Add TimeSeries.descriptionLazy field to test that reading string attributes works

* Added function getGroupObjects to the I/O to allow gettings all objects contained in a group

* Add BaseIO::findTypes method to search for neurodata_types in a file

* Add example for searching for typed objects

* Update ElectrodeTable to use m_ member names

* Added field definitions for TimeSeries

* Add some design docs about reading registered types

* Update read types design figure

* Minor update to docs of RegisteredType

* Update docs/pages/userdocs/read.dox

Co-authored-by: Steph Prince <[email protected]>

* Update src/io/ReadIO.hpp

Co-authored-by: Steph Prince <[email protected]>

* Update src/io/ReadIO.hpp

* Rename ReadDataWrapper.is_dataset

* Update src/io/hdf5/HDF5IO.hpp

Co-authored-by: Steph Prince <[email protected]>

* Remove duplicate code in NWBFile

* Remove outdated ToDo item

* Apply suggestions on docs from code review

Co-authored-by: Steph Prince <[email protected]>

* Fix build error after merge

* Fix build error due to error during merge with base branch

* Apply suggestions from code review

Co-authored-by: Steph Prince <[email protected]>

* Fix read.dox figure based on suggestion

* Fix docs based on code review

* update getAttribute method to detect object type

* update read example

* add file mode options and readonly mode to io

* update example to use readonly

* add getter functions to Container classes with attributes

* fix formatting

* update lint workflow

* add NWBFile fields

* update filenames

* remove duplicate file mode checks

* Fix spelling and complete merge

* Fix missing merge in file

* Install Boost multi-array in windows action

* Use std::vector instead of variable-length arrays to avoid Windows built errors

* Add note to docs to clarify non-virtual DEFINE_FIELD

* Add HDF5IO tests for add_read PR (#118)

* Add unit test for HDFIO::getH5ObjectType

* Fix behavior of HDF5IO::getH5ObjectType when object does not exist

* Add unit tests for HDF5IO::getNativeType

* Add unit test for HDF5IO::getH5Type

* Add unit test for HDF5IO::objectExists

* Add unit tests for HDF5IO::attributeExists

* add unit test for HDF5IO::getGroupObjects

* Add unit test for HDF5IO::getStorageObjectType

* Fix reading of arrays and select dtypes for HDF5IO::readAttribute

* Add initial unit tests for HDF5IO::readAttribute

* Fix reading of string array attributes via HDF5IO::readAttribute

* Fix reading of reference attributes

* Add HDF5IO::readAttribute unit test for invalid and double attribute

* Add HDF5IO::readAttriute unit test for reading attribute from a dataset

* Fix handling of non-existent file ReadOnly and ReadWrite mode

* Add unit tests for HDF5IO::open for file modes

* Add additonal unit tests for HDF5IO::readDataset

* Fix element selection in HDF5IO::readDataset

* Add unit test for reading hyperslap with HDF5IO::readDataset

* Fix hyperslap selection with block in HDF5IO::readDataset

* Expand unit tests for HDF5IO::readDataset with hyperslaps

* Add note with TODO item to the tests

* Improve coverage of numeric types in readDataset

* Fix determining string size on read

* Set string size when creating the dataset

* Fix error in commmented unit test

* Fix writing of fixed length string datasets

* Add test writing fixed length string data

* Fix reading of fixed length strings

* Fix writing of variable length string datasets

* Fix writing of variable length string datasets

* Fix reading of variable length string dataset

* Fix reading of empty string datasets

* Minor enhancement to check for address santizer

* Create RecordingData::writeStrinData to simplify writing of strings

* Update tests/testHDF5IO.cpp

Co-authored-by: Steph Prince <[email protected]>

* Update tests/testHDF5IO.cpp

Co-authored-by: Steph Prince <[email protected]>

* Fix formatting

---------

Co-authored-by: Steph Prince <[email protected]>

* Update read example

* Add missing offset attriubte for TimeSeries.data

* Add basic unit test for TimeSeries read

* Add more REQUIRE checks

* Add missing TimeSerires.conitnuity field for write. Move timeseries-specific write method from BaseIO to TimeSeries

* Fix spellcheck errors

* Add read test for timestamps.unit and timestamps.interval

* Support using starting_time and rate with TimeSeries

* Add missing timeseries control and control description. Fix chunking of timestamps

* Move neurodata_type and namespace attributes to Container

* address validation errors in add_read (#125)

* use writeStringDataBlock function

* update SES test to use multiple channels

* update filename to avoid access errors

* fix formatting

* Fix #126 Initiallization of VectorData

* Renamed testBase to testTimeSeries

* Add initial unit tests for VectorData

* Add additional unit tests for VectorData

* Add write/read test for Data

* Add write/read test for ElementIdentifiers

* Add unit test for Device

* Remove extra comment

* Remove extra comments

* Update src/io/hdf5/HDF5IO.cpp

Co-authored-by: Steph Prince <[email protected]>

* Update tests/testTimeSeries.cpp

Co-authored-by: Steph Prince <[email protected]>

* Move test to RegisteredType

* Update writing of control_description

* Fix code linting

* Add pass-through for control to RecordingContainers::write.. methods

* Minor updates to the example

* Fix file name collision in tests

* Make calling io->open() explicit

* Adjust slice to check if it fixes Windows error

* Add output to help debug Windows issue

* Add debug output in HDF5IO to debug Windows issue

* Update NWBFile.hpp

* Fix code formatting

* Some more debug ouput for Windows

* Fix read path to avoid potentially reading from different series

* Clean up deug output

---------

Co-authored-by: Steph Prince <[email protected]>
  • Loading branch information
oruebel and stephprince authored Jan 13, 2025
1 parent a532ec5 commit 354147f
Show file tree
Hide file tree
Showing 71 changed files with 7,424 additions and 1,761 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/codespell.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ on:
- main

pull_request:
branches:
- main
#branches:
#- main
workflow_dispatch:

jobs:
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/doxygen-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ name: Doxygen build test

on:
pull_request:
branches:
- main
#branches:
#- main

jobs:
test:
Expand All @@ -17,7 +17,7 @@ jobs:
uses: actions/checkout@v4
with:
submodules: "true"

- name: Install dependencies - macos
run: brew install hdf5 boost catch2 doxygen graphviz

Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ on:
- main

pull_request:
branches:
- main
#branches:
#- main
workflow_dispatch:

jobs:
Expand Down
10 changes: 5 additions & 5 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ on:
- main

pull_request:
branches:
- main
#branches:
#- main
workflow_dispatch:

jobs:
Expand Down Expand Up @@ -39,7 +39,7 @@ jobs:
git checkout "v3.5.3"
cmake -Bbuild -H. -DBUILD_TESTING=OFF
sudo cmake --build build/ --target install
- name: Install dependencies - macos
if: matrix.os == 'macos-latest'
run: brew install hdf5 boost catch2
Expand All @@ -48,7 +48,7 @@ jobs:
if: matrix.os == 'windows-latest'
run: |
cd "${VCPKG_INSTALLATION_ROOT}"
vcpkg install hdf5[cpp]:x64-windows boost-date-time:x64-windows boost-endian:x64-windows boost-uuid:x64-windows catch2:x64-windows
vcpkg install hdf5[cpp]:x64-windows boost-date-time:x64-windows boost-endian:x64-windows boost-uuid:x64-windows boost-multi-array:x64-windows catch2:x64-windows
vcpkg integrate install
- name: Configure
Expand All @@ -74,7 +74,7 @@ jobs:
sanitize:
needs: test

runs-on: ubuntu-latest

steps:
Expand Down
9 changes: 7 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -19,19 +19,24 @@ include(cmake/variables.cmake)

add_library(
aqnwb_aqnwb
src/BaseIO.cpp
src/io/BaseIO.cpp
src/Channel.cpp
src/hdf5/HDF5IO.cpp
src/io/hdf5/HDF5IO.cpp
src/io/hdf5/HDF5RecordingData.cpp
src/nwb/NWBFile.cpp
src/nwb/RecordingContainers.cpp
src/nwb/RegisteredType.cpp
src/nwb/base/TimeSeries.cpp
src/nwb/device/Device.cpp
src/nwb/ecephys/ElectricalSeries.cpp
src/nwb/ecephys/SpikeEventSeries.cpp
src/nwb/file/ElectrodeGroup.cpp
src/nwb/file/ElectrodeTable.cpp
src/nwb/hdmf/base/Container.cpp
src/nwb/hdmf/base/Data.cpp
src/nwb/hdmf/table/DynamicTable.cpp
src/nwb/hdmf/table/VectorData.cpp
src/nwb/hdmf/table/ElementIdentifiers.cpp
)

add_library(aqnwb::aqnwb ALIAS aqnwb_aqnwb)
Expand Down
20 changes: 20 additions & 0 deletions docs/Doxyfile.in
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,34 @@
PROJECT_NAME = "@PROJECT_NAME@"
PROJECT_NUMBER = "@PROJECT_VERSION@"


# Expand the DEFINE_FIELD macro to create documentation also for functions created by
# this macro as part of the RegisterType.hpp logic, which simplifies the creation
# of functions for lazy read access. Unfortunately, expanding the macro directly
# from the source code directly for some reason did not work with Doxygen and
# using "EXPAND_AS_DEFINED = DEFINE_FIELD" also failed. As a workaround we here define
# a simplified version of the macro as part of the PREDEFINED key to create a
# simplified expansion of the macro for documentation purposes
MACRO_EXPANSION = YES
PREDEFINED += "DEFINE_FIELD(name, storageObjectType, default_type, fieldPath, description)=/** description */ template<typename VTYPE = default_type> inline std::unique_ptr<IO::ReadDataWrapper<storageObjectType, VTYPE>> name() const;"
EXPAND_ONLY_PREDEF = YES


# Add sources
INPUT = "@PROJECT_SOURCE_DIR@/src" "@PROJECT_SOURCE_DIR@/docs/pages"
RECURSIVE = YES
EXAMPLE_PATH = "@PROJECT_SOURCE_DIR@/tests" "@PROJECT_SOURCE_DIR@/.github/CODE_OF_CONDUCT.md" "@PROJECT_SOURCE_DIR@/Legal.txt" "@PROJECT_SOURCE_DIR@/LICENSE"
IMAGE_PATH = "@PROJECT_SOURCE_DIR@/resources/images"
EXTRACT_ALL = YES
RECURSIVE = YES
OUTPUT_DIRECTORY = "@DOXYGEN_OUTPUT_DIRECTORY@"

# Also show private members in the docs,
EXTRACT_PRIVATE = YES
# Also show static members in the docs
EXTRACT_STATIC = YES
# HIDE_UNDOC_MEMBERS = YES

# Enable Markdown support
MARKDOWN_SUPPORT = YES

Expand Down
1 change: 1 addition & 0 deletions docs/pages/1_userdocs.dox
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@
*
* - \subpage user_install_page
* - \subpage workflow
* - \subpage read_page
* - \subpage hdf5io
*/
1 change: 1 addition & 0 deletions docs/pages/2_devdocs.dox
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
* - \subpage testing
* - \subpage dev_docs_page
* - \subpage nwb_schema_page
* - \subpage registered_type_page
* - \subpage code_of_conduct_page
* - \subpage license_page
* - \subpage copyright_page
Expand Down
2 changes: 1 addition & 1 deletion docs/pages/devdocs/documentation.dox
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@
*
* To display a code snippet from our example in the documentation we can use the
* ``\snippet <file> <label>`` Doxygen command in our ``*.dox`` documentation file.
* For example to just show the line where we create the \ref AQNWB::HDF5::HDF5IO "HDF5IO
* For example to just show the line where we create the \ref AQNWB::IO::HDF5::HDF5IO "HDF5IO"
* object we use:
*
* \code{.sh}
Expand Down
215 changes: 215 additions & 0 deletions docs/pages/devdocs/registered_types.dox
Original file line number Diff line number Diff line change
@@ -0,0 +1,215 @@
/**
*
* \page registered_type_page Implementing a new Neurodata Type
*
* \tableofcontents
*
*
*
* New neurodata_types typically inherit from at least either \ref AQNWB::NWB::Container "Container"
* or \ref AQNWB::NWB::Data "Data", or a more specialized type of the two. In any case,
* all classes that represent a ``neurodata_type`` defined in the schema should be implemented
* as a subtype of \ref AQNWB::NWB::RegisteredType "RegisteredType".
*
* @section implement_registered_type How to Implement a RegisteredType
*
* To implement a subclass of \ref AQNWB::NWB::RegisteredType "RegisteredType", follow these steps:
*
* 1. Include the `RegisteredType.hpp` header file in your subclass header file.
* @code
* #include "nwb/RegisteredType.hpp"
* @endcode
*
* 2. Define your subclass by inheriting from \ref AQNWB::NWB::RegisteredType "RegisteredType".
* Ensure that your subclass implements a constructor with the arguments
* `(const std::string& path, std::shared_ptr<IO::BaseIO> io)`,
* as the "create" method expects this constructor signature.
* @code
* class MySubClass : public AQNWB::NWB::RegisteredType {
* public:
* MySubClass(const std::string& path, std::shared_ptr<IO::BaseIO> io)
* : RegisteredType(path, io) {}
*
* // Implement any additional methods or overrides here
* };
* @endcode
*
* 3. Use the \ref REGISTER_SUBCLASS macro to register your subclass. This should usually appear in
* the header (`hpp`) file as part of the class definition:
* @code
* REGISTER_SUBCLASS(MySubClass, "my-namespace")
* @endcode
*
* 4. In the corresponding source (`cpp`) file, initialize the static member to trigger the registration
* using the \ref REGISTER_SUBCLASS_IMPL macro:
* @code
* #include "MySubClass.h"
*
* // Initialize the static member to trigger registration
* REGISTER_SUBCLASS_IMPL(MySubClass)
* @endcode
*
* 5. To define getter methods for lazy read access to datasets and attributes that belong to our type,
* we can use the \ref DEFINE_FIELD macro. This macro creates a standard method for retrieving a
* \ref AQNWB::IO::ReadDataWrapper "ReadDataWrapper" for lazy reading for the field:
* @code
* DEFINE_FIELD(getData, DatasetField, float, "data", The main data)
* @endcode
*
* \warning
* To ensure proper function on read, the name of the class should match the name of the
* ``neurodata_type`` as defined in the schema. Similarly, "my-namespace" should match
* the name of the namespace in the schema (e.g., "core", "hdmf-common"). In this way
* we can look up the corresponding class for an object in a file based on the
* ``neurodata_type`` and ``namespace`` attributes stored in the file.
*
* \note
* A special version of the ``REGISTER_SUBCLASS`` macro, called ``REGISTER_SUBCLASS_WITH_TYPENAME``,
* allows setting the typename explicitly as a third argument. This is for the **special case**
* where we want to implement a class for a modified type that does not have its
* own `neurodata_type` in the NWB schema. An example is `ElectrodesTable` in NWB <v2.7, which
* did not have an assigned `neurodata_type`, but was implemented as a regular
* `DynamicTable`. To allow us to define a class `ElectrodeTable` to help with writing the table
* we can then use ``REGISTER_SUBCLASS_WITH_TYPENAME(ElectrodeTable, "core", "DynamicTable")``
* in the `ElectrodesTable` class. This ensures that the `neurodata_type` attribute is set
* correctly to `DynamicTable` on write instead of `ElectrodesTable`. However, on read this
* will by default not use the `ElectrodesTable` class but the regular `DynamicTable` class
* since that is what the schema is indicating to use. In the registry, the class will still
* be registered under the ``core::ElectrodesTable`` key, but with "DynamicTable" as the
* typename value and the `ElectrodesTable.getTypeName` automatic override returning
* the indicated typename instead of the classname.
*
* \note
* ``DEFINE_FIELD`` creates templated, non-virtual read functions. This means if we
* want to "redefine" a field in a child class by calling ``DEFINE_FIELD`` again, then
* the function will be "hidden" instead of "override". This is important to remember
* when casting a pointer to a base type, as in this case the implementation from the
* base type will be used since the function created by ``DEFINE_FIELD`` is not virtual.
*
*
* @subsection implement_registered_type_example Example: Implementing a new type
*
* *MySubClass.hpp*
* @code
* #pragma once
* #include "RegisteredType.hpp"
*
* class MySubClass : public AQNWB::NWB::RegisteredType
* {
* public:
* MySubClass(const std::string& path, std::shared_ptr<IO::BaseIO> io)
* : RegisteredType(path, io) {}
*
* DEFINE_FIELD(getData, DatasetField, float, "data", The main data)
*
* REGISTER_SUBCLASS(MySubClass, "my-namespace")
* };
* @endcode
*
* *MySubClass.cpp*
* @code
* #include "MySubClass.h"
*
* // Initialize the static member to trigger registration
* REGISTER_SUBCLASS_IMPL(MySubClass)
* @endcode
*
* @section type_registry How the Type Registry in RegisteredType Works
*
* The type registry in \ref AQNWB::NWB::RegisteredType "RegisteredType" allows for dynamic creation of registered subclasses by name. Here is how it works:
*
* 1. **Registry Storage**:
* - The registry is implemented using 1) an `std::unordered_set` to store subclass names (which can be
* accessed via \ref AQNWB::NWB::RegisteredType::getRegistry "getRegistry()") and
* 2) an `std::unordered_map` to store factory functions for creating instances of the subclasses
* (which can be accessed via \ref AQNWB::NWB::RegisteredType::getFactoryMap() "getFactoryMap()").
* The factory methods are the required constructor that uses the io and path as input.
* - These are defined as static members within the \ref AQNWB::NWB::RegisteredType "RegisteredType" class.
*
* 2. **Registration**:
* - The \ref AQNWB::NWB::RegisteredType::registerSubclass "registerSubclass" method is used to add a
* subclass name and its corresponding factory function to the registry.
* - This method is called via the `REGISTER_SUBCLASS` macro, which defines a static method (`registerSubclass()`)
* and static member (`registered_`) to trigger the registration when the subclass is loaded.
*
* 3. **Dynamic Creation**:
* - The \ref AQNWB::NWB::RegisteredType::create "create" method is used to create an instance of a registered subclass by name.
* - This method looks up the subclass name in the registry and calls the corresponding factory function to create an instance.
*
* 4. **Automatic Registration**:
* - The `REGISTER_SUBCLASS_IMPL` macro initializes the static member (`registered_`), which triggers the
* \ref AQNWB::NWB::RegisteredType::registerSubclass "registerSubclass" method
* and ensures that the subclass is registered when the program starts.
*
* 5. **Class Name and Namespace Retrieval**:
* - The \ref AQNWB::NWB::RegisteredType::getTypeName "getTypeName" and
* \ref AQNWB::NWB::RegisteredType::getNamespace "getNamespace" return the string name of the
* class and namespace, respectively. The `REGISTER_SUBCLASS` macro implements an automatic
* override of the methods to ensure the appropriate type and namespace string are returned.
* These methods should, hence, not be manually overridden by subclasses, to ensure consistency
* in type identification.
*
*
* @section use_registered_type_registry How to Use the RegisteredType Registry
*
* The \ref AQNWB::NWB::RegisteredType "RegisteredType" registry allows for dynamic creation and management of registered subclasses. Here is how you can use it:
*
* 1. **Creating Instances Dynamically**:
* - Use the \ref AQNWB::NWB::RegisteredType::create "create" method to create an instance of a registered subclass by name.
* - This method takes the subclass name, path, and a shared pointer to the IO object as arguments. This
* illustrates how we can read a specific typed object in an NWB file.
* \snippet tests/examples/test_RegisteredType_example.cpp example_RegisterType_get_type_instance
*
* 2. **Retrieving Registered Subclass Names**:
* - Use the \ref AQNWB::NWB::RegisteredType::getRegistry "getRegistry" method to retrieve the
* set of registered subclass names.
* \snippet tests/examples/test_RegisteredType_example.cpp example_RegisterType_get_registered_names
*
* 3. **Retrieving the Factory Map**:
* - Use the \ref AQNWB::NWB::RegisteredType::getFactoryMap "getFactoryMap" method to retrieve
* the map of factory functions for creating instances of registered subclasses.
* \snippet tests/examples/test_RegisteredType_example.cpp example_RegisterType_get_registered_factories
*
* @subsection use_registered_type_registry_example Example: Using the type registry
*
* \snippet tests/examples/test_RegisteredType_example.cpp example_RegisterType_full
*
* @section use_the_define_field_macro How to Use the DEFINE_FIELD macro
*
* The \ref DEFINE_FIELD macro takes the following main inputs:
*
* * ``name``: The name of the function to generate.
* * ``storageObjectType`` : One of either \ref AQNWB::NWB::DatasetField "DatasetField" or
* \ref AQNWB::NWB::AttributeField "AttributeField" to define the type of storage object used
* to store the field.
* * ``default_type`` : The default data type to use. If not known, we can use ``std::any``.
* * ``fieldPath`` : Literal string with the relative path to the field within the schema of the
* respective neurodata_type. This is automatically being expanded at runtime to the full path.
* * ``description`` : Description of the field to include in the docstring for the docs
*
* All of these inputs are required. A typical example will look as follows:
*
* @code
* DEFINE_FIELD(getData, DatasetField, float, "data", The main data)
* @endcode
*
* The compiler will then expand this definition to create a new method
* called ``getData`` that will return a \ref AQNWB::IO::ReadDataWrapper "ReadDataWrapper"
* for lazy reading for the field. The corresponding expanded function will look something like:
*
* @code
* template<typename VTYPE = float>
* inline std::unique_ptr<IO::ReadDataWrapper<DatasetField, VTYPE>> getData() const
* {
* return std::make_unique<IO::ReadDataWrapper<DatasetField, VTYPE>>(
* m_io,
* AQNWB::mergePaths(m_path, fieldPath));
* }
* @endcode
*
* See \ref read_page for an example of how to use such methods (e.g.,
* \ref AQNWB::NWB::TimeSeries::readData "TimeSeries::readData" )
* for reading data fields from a file.
*
*
*/
Loading

0 comments on commit 354147f

Please sign in to comment.