diff --git a/.gitignore b/.gitignore
index c490026..18f8657 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,2 @@
obj/
-tools/*.svg
build/
\ No newline at end of file
diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json
new file mode 100644
index 0000000..58f0c9f
--- /dev/null
+++ b/.vscode/c_cpp_properties.json
@@ -0,0 +1,28 @@
+{
+ "configurations": [
+ {
+ "name": "Linux",
+ "browse": {
+ "path": [
+ "${workspaceFolder}",
+ "${workspaceFolder}/src/*"
+ ],
+ "limitSymbolsToIncludedHeaders": true
+ },
+ "includePath": [
+ "${default}",
+ "/usr/include/SFML",
+ "/usr/lib/x86_64-linux-gnu",
+ "/usr/include",
+ "/usr/include/opencv4",
+ "${workspaceFolder}/src/include"
+ ],
+ "defines": [],
+ "cStandard": "c17",
+ "cppStandard": "gnu++17",
+ "intelliSenseMode": "linux-gcc-x64",
+ "configurationProvider": "ms-vscode.makefile-tools"
+ }
+ ],
+ "version": 4
+}
\ No newline at end of file
diff --git a/.vscode/launch.json b/.vscode/launch.json
new file mode 100644
index 0000000..b276ab2
--- /dev/null
+++ b/.vscode/launch.json
@@ -0,0 +1,33 @@
+{
+ "configurations": [
+ {
+ "name": "C/C++: g++-11 build and debug active file",
+ "type": "cppdbg",
+ "request": "launch",
+ "program": "${workspaceFolder}/bin/Debug/biosim4",
+ "args": [
+ "${workspaceFolder}/biosim4.ini"
+ ],
+ "cwd": "${workspaceFolder}",
+ "stopAtEntry": false,
+ "environment": [],
+ "externalConsole": false,
+ "MIMode": "gdb",
+ "setupCommands": [
+ {
+ "description": "Enable pretty-printing for gdb",
+ "text": "-enable-pretty-printing",
+ "ignoreFailures": true
+ },
+ {
+ "description": "Set Disassembly Flavor to Intel",
+ "text": "-gdb-set disassembly-flavor intel",
+ "ignoreFailures": true
+ }
+ ],
+ "preLaunchTask": "${defaultBuildTask}",
+ "miDebuggerPath": "/usr/bin/gdb"
+ }
+ ],
+ "version": "2.0.0"
+}
\ No newline at end of file
diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 0000000..575bc42
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,77 @@
+{
+ "files.associations": {
+ "*.embeddedhtml": "html",
+ "array": "cpp",
+ "atomic": "cpp",
+ "bit": "cpp",
+ "*.tcc": "cpp",
+ "cctype": "cpp",
+ "chrono": "cpp",
+ "clocale": "cpp",
+ "cmath": "cpp",
+ "compare": "cpp",
+ "complex": "cpp",
+ "concepts": "cpp",
+ "condition_variable": "cpp",
+ "cstdarg": "cpp",
+ "cstddef": "cpp",
+ "cstdint": "cpp",
+ "cstdio": "cpp",
+ "cstdlib": "cpp",
+ "cstring": "cpp",
+ "ctime": "cpp",
+ "cwchar": "cpp",
+ "cwctype": "cpp",
+ "deque": "cpp",
+ "list": "cpp",
+ "map": "cpp",
+ "set": "cpp",
+ "string": "cpp",
+ "unordered_map": "cpp",
+ "vector": "cpp",
+ "exception": "cpp",
+ "algorithm": "cpp",
+ "functional": "cpp",
+ "iterator": "cpp",
+ "memory": "cpp",
+ "memory_resource": "cpp",
+ "numeric": "cpp",
+ "random": "cpp",
+ "ratio": "cpp",
+ "string_view": "cpp",
+ "system_error": "cpp",
+ "tuple": "cpp",
+ "type_traits": "cpp",
+ "utility": "cpp",
+ "fstream": "cpp",
+ "initializer_list": "cpp",
+ "iomanip": "cpp",
+ "iosfwd": "cpp",
+ "iostream": "cpp",
+ "istream": "cpp",
+ "limits": "cpp",
+ "mutex": "cpp",
+ "new": "cpp",
+ "numbers": "cpp",
+ "ostream": "cpp",
+ "semaphore": "cpp",
+ "sstream": "cpp",
+ "stdexcept": "cpp",
+ "stop_token": "cpp",
+ "streambuf": "cpp",
+ "thread": "cpp",
+ "cinttypes": "cpp",
+ "typeinfo": "cpp",
+ "cassert": "cpp",
+ "any": "cpp",
+ "bitset": "cpp",
+ "codecvt": "cpp",
+ "unordered_set": "cpp",
+ "optional": "cpp",
+ "regex": "cpp",
+ "typeindex": "cpp",
+ "variant": "cpp",
+ "forward_list": "cpp",
+ "valarray": "cpp"
+ }
+}
\ No newline at end of file
diff --git a/.vscode/tasks.json b/.vscode/tasks.json
new file mode 100644
index 0000000..a6a3c5d
--- /dev/null
+++ b/.vscode/tasks.json
@@ -0,0 +1,20 @@
+{
+ "tasks": [
+ {
+ "type": "cppbuild",
+ "label": "C/C++: g++ build active file",
+ "command": "make debug",
+ "args": [
+ ],
+ "options": {
+ "cwd": "${workspaceFolder}/"
+ },
+ "problemMatcher": [
+ "$gcc"
+ ],
+ "group": "build",
+ "detail": "Task generated by Debugger."
+ }
+ ],
+ "version": "2.0.0"
+}
\ No newline at end of file
diff --git a/Makefile b/Makefile
index dc46d3a..ee607d3 100644
--- a/Makefile
+++ b/Makefile
@@ -11,6 +11,7 @@ CXXFLAGS += \
-std=c++17 \
-fexceptions \
-fopenmp \
+ -I./src/include \
$(shell pkg-config --cflags opencv4)
LDFLAGS += \
@@ -19,6 +20,10 @@ LDFLAGS += \
-lopencv_videoio \
-lgomp \
-lpthread \
+ -lsfml-graphics \
+ -lsfml-window \
+ -lsfml-system \
+ -ltgui \
-fopenmp
ifeq ($(BUILD),debug)
@@ -32,10 +37,19 @@ else
LDFLAGS += -O3 -s
endif
-SOURCE := $(wildcard src/*.cpp src/*.h)
+SOURCE := $(wildcard src/*.cpp src/*.h src/userio/*.cpp src/userio/*.h \
+ src/userio/sfmlComponents/*.cpp src/userio/sfmlComponents/*.h \
+ src/utils/*.cpp src/utils/*.h \
+ src/ai/*.cpp src/ai/*.h \
+ src/survivalCriteria/*.cpp src/survivalCriteria/*.h \
+ src/userio/sfmlComponents/flowComponents/*.cpp src/userio/sfmlComponents/flowComponents/*.h \
+ src/userio/sfmlComponents/settingsComponents/*.cpp src/userio/sfmlComponents/settingsComponents/*.h \
+ )
CXXSOURCE := $(filter %.cpp, $(SOURCE))
HEADERS := $(filter %.h, $(SOURCE))
OBJS := $(subst src/,$(OBJ_DIR)/, $(CXXSOURCE:.cpp=.o))
+INCLUDES = -I./src/include
+LIBS = -L/path/to/cereal/lib -lcereal
all: debug release
@@ -44,10 +58,24 @@ all: debug release
before_debug:
test -d bin/Debug || mkdir -p bin/Debug
test -d obj/Debug/src || mkdir -p obj/Debug/src
+ test -d obj/Debug/src/userio || mkdir -p obj/Debug/src/userio
+ test -d obj/Debug/src/userio/sfmlComponents || mkdir -p obj/Debug/src/userio/sfmlComponents
+ test -d obj/Debug/src/userio/sfmlComponents/flowComponents || mkdir -p obj/Debug/src/userio/sfmlComponents/flowComponents
+ test -d obj/Debug/src/userio/sfmlComponents/settingsComponents || mkdir -p obj/Debug/src/userio/sfmlComponents/settingsComponents
+ test -d obj/Debug/src/utils || mkdir -p obj/Debug/src/utils
+ test -d obj/Debug/src/ai || mkdir -p obj/Debug/src/ai
+ test -d obj/Debug/src/survivalCriteria || mkdir -p obj/Debug/src/survivalCriteria
before_release:
test -d bin/Release || mkdir -p bin/Release
test -d obj/Release/src || mkdir -p obj/Release/src
+ test -d obj/Release/src/userio || mkdir -p obj/Release/src/userio
+ test -d obj/Release/src/userio/sfmlComponents || mkdir -p obj/Release/src/userio/sfmlComponents
+ test -d obj/Release/src/userio/sfmlComponents/flowComponents || mkdir -p obj/Release/src/userio/sfmlComponents/flowComponents
+ test -d obj/Release/src/userio/sfmlComponents/settingsComponents || mkdir -p obj/Release/src/userio/sfmlComponents/settingsComponents
+ test -d obj/Release/src/utils || mkdir -p obj/Release/src/utils
+ test -d obj/Release/src/ai || mkdir -p obj/Release/src/ai
+ test -d obj/Release/src/survivalCriteria || mkdir -p obj/Release/src/survivalCriteria
.PHONY : release debug
debug: before_debug
@@ -77,7 +105,7 @@ clean_release:
$(RM) -f bin/Release/biosim4
distclean: clean
- $(RM) -f logs/* images/*
+ $(RM) -f Output/Images/* Output/Videos/* Output/Logs/* Output/Saves/* Output/Profiling/*
.PHONY: all clean distclean
diff --git a/images/.gitignore b/Output/Images/.gitignore
similarity index 100%
rename from images/.gitignore
rename to Output/Images/.gitignore
diff --git a/logs/.gitignore b/Output/Logs/.gitignore
similarity index 100%
rename from logs/.gitignore
rename to Output/Logs/.gitignore
diff --git a/Output/Profiling/.gitignore b/Output/Profiling/.gitignore
new file mode 100644
index 0000000..76bedae
--- /dev/null
+++ b/Output/Profiling/.gitignore
@@ -0,0 +1,5 @@
+# Ignore everything in this directory
+*
+# Except this file
+!.gitignore
+
diff --git a/Output/Saves/.gitignore b/Output/Saves/.gitignore
new file mode 100644
index 0000000..76bedae
--- /dev/null
+++ b/Output/Saves/.gitignore
@@ -0,0 +1,5 @@
+# Ignore everything in this directory
+*
+# Except this file
+!.gitignore
+
diff --git a/Output/Videos/.gitignore b/Output/Videos/.gitignore
new file mode 100644
index 0000000..76bedae
--- /dev/null
+++ b/Output/Videos/.gitignore
@@ -0,0 +1,5 @@
+# Ignore everything in this directory
+*
+# Except this file
+!.gitignore
+
diff --git a/README.md b/README.md
index dccd319..0adec05 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,32 @@
# biosim4
+Fork of https://github.com/davidrmiller/biosim4
+
+### What has been done
+
+- heavy refactoring
+- a new output via SFML which renders individuals, challenge criterias and barriers in real-time with the ability to drag simulation (LMB), zoom in/out (scroll), select indiv (LMB)
+- UI to watch and control simulation with TGUI:
+ - pause/resume button, pause at start/end of generation
+ - slow or increase speed of simulation
+ - change some settings
+ - restart simulation
+ - save selected indiv's net to svg
+ - show indivs who would pass survival criteria if simulation end now
+- saving and loading simulations with "cereal" serialization library
+
+![screenshot](https://github.com/ilyabrilev/biosim4/blob/main/screenshot.png?raw=true)
+
+### Plans
+- expand UI functionality to controll simulation
+- implement recording of simulation, add ability to play it with time skipping, ability to go back (etc.)
+- hopefuly add more behaviour, events, resources to simulation
+
+### Requirements
+- sfml https://stackoverflow.com/questions/30696114/how-to-install-sfml-in-ubuntu
+- tgui 1.1 https://tgui.eu/tutorials/latest-stable/
+- cereal (it should work right of the box, but here is a link anyway https://uscilab.github.io/cereal/index.html)
+
## What is this?
This pile of code was used to simulate biological creatures that evolve through natural selection.
diff --git a/Resources/Black.png b/Resources/Black.png
new file mode 100644
index 0000000..139cab2
Binary files /dev/null and b/Resources/Black.png differ
diff --git a/Resources/Black.txt b/Resources/Black.txt
new file mode 100644
index 0000000..925cbac
--- /dev/null
+++ b/Resources/Black.txt
@@ -0,0 +1,154 @@
+TextColor = rgb(190, 190, 190);
+TextColorHover = rgb(250, 250, 250);
+SelectedTextColor = White;
+BorderColor = Black;
+BackgroundColor = rgb(80, 80, 80);
+BackgroundColorHover = rgb(100, 100, 100);
+SelectedBackgroundColor = rgb(10, 110, 255);
+SelectedBackgroundColorHover = rgb(30, 150, 255);
+
+Button {
+ Texture = "Black.png" Part(140, 53, 45, 50) Middle(10, 0) Smooth;
+ TextureHover = "Black.png" Part(140, 1, 45, 50) Middle(10, 0) Smooth;
+ TextureDown = "Black.png" Part(125, 105, 45, 50) Middle(10, 0) Smooth;
+ TextureFocused = "Black.png" Part(93, 53, 45, 50) Middle(10, 0) Smooth;
+}
+
+ChatBox {
+ TextureBackground = "Black.png" Part(63, 146, 48, 48) Middle(16) Smooth;
+ Padding = (3, 3, 3, 3);
+}
+
+CheckBox {
+ TextureUnchecked = "Black.png" Part(125, 209, 32, 32) Smooth;
+ TextureChecked = "Black.png" Part(219, 171, 32, 32) Smooth;
+ TextureUncheckedHover = "Black.png" Part(221, 35, 32, 32) Smooth;
+ TextureCheckedHover = "Black.png" Part(221, 1, 32, 32) Smooth;
+ TextureUncheckedFocused = "Black.png" Part(216, 209, 32, 32) Smooth;
+ TextureCheckedFocused = "Black.png" Part(221, 69, 32, 32) Smooth;
+}
+
+ChildWindow {
+ TextureTitleBar = "Black.png" Part(1, 85, 75, 25) Middle(25, 0) Smooth;
+ Borders = (1, 1, 1, 1);
+ DistanceToSide = 5;
+ PaddingBetweenButtons = 2;
+ ShowTextOnTitleButtons = false;
+ CloseButton = {
+ Texture = "Black.png" Part(79, 238, 15, 15) Smooth;
+ TextureHover = "Black.png" Part(255, 40, 15, 15) Smooth;
+ TextureDown = "Black.png" Part(45, 230, 15, 15) Smooth;
+ };
+ MaximizeButton = {
+ Texture = "Black.png" Part(255, 23, 15, 15) Smooth;
+ TextureHover = "Black.png" Part(267, 239, 15, 15) Smooth;
+ TextureDown = "Black.png" Part(62, 238, 15, 15) Smooth;
+ };
+ MinimizeButton = {
+ Texture = "Black.png" Part(255, 57, 15, 15) Smooth;
+ TextureHover = "Black.png" Part(250, 239, 15, 15) Smooth;
+ TextureDown = "Black.png" Part(96, 238, 15, 15) Smooth;
+ };
+}
+
+ComboBox {
+ TextureBackground = "Black.png" Part(63, 146, 48, 48) Middle(16) Smooth;
+ TextureArrow = "Black.png" Part(250, 205, 32, 32) Smooth;
+ TextureArrowHover = "Black.png" Part(219, 103, 32, 32) Smooth;
+ Padding = (3, 3, 3, 3);
+}
+
+EditBox {
+ Texture = "Black.png" Part(1, 154, 60, 40) Middle(15, 0) Smooth;
+ TextureHover = "Black.png" Part(63, 196, 60, 40) Middle(15, 0) Smooth;
+ TextureFocused = "Black.png" Part(1, 112, 60, 40) Middle(15, 0) Smooth;
+
+ DefaultTextColor = rgb(120, 120, 120);
+ CaretColor = rgb(110, 110, 255);
+ Padding = (6, 4, 6, 4);
+}
+
+Knob {
+ Borders = (2, 2, 2, 2);
+}
+
+ListBox {
+ TextureBackground = "Black.png" Part(63, 146, 48, 48) Middle(16) Smooth;
+ Padding = (3, 3, 3, 3);
+}
+
+ListView {
+ HeaderBackgroundColor = rgb( 95, 95, 95);
+ HeaderTextColor = rgb(210, 210, 210);
+ Borders = (1, 1, 1, 1);
+}
+
+MenuBar {
+ TextureBackground = "Black.png" Part(45, 247, 8, 6) Middle(2, 2) NoSmooth;
+ TextureItemBackground = "Black.png" Part(78, 105, 8, 4) Middle(2, 0, 4, 2) NoSmooth;
+ TextureSelectedItemBackground = "Black.png" Part(113, 238, 8, 6) Middle(2, 2) NoSmooth;
+ DistanceToSide = 5;
+}
+
+ProgressBar {
+ TextureBackground = "Black.png" Part(1, 1, 90, 40) Middle(20, 0) Smooth;
+ TextureFill = "Black.png" Part(1, 43, 90, 40) Middle(16, 0) Smooth;
+ TextColorFilled = rgb(250, 250, 250);
+}
+
+RadioButton {
+ TextureUnchecked = "Black.png" Part(219, 137, 32, 32) Smooth;
+ TextureChecked = "Black.png" Part(187, 35, 32, 32) Smooth;
+ TextureUncheckedHover = "Black.png" Part(187, 69, 32, 32) Smooth;
+ TextureCheckedHover = "Black.png" Part(253, 137, 32, 32) Smooth;
+ TextureUncheckedFocused = "Black.png" Part(253, 103, 32, 32) Smooth;
+ TextureCheckedFocused = "Black.png" Part(187, 1, 32, 32) Smooth;
+}
+
+Scrollbar {
+ TextureTrack = "Black.png" Part(255, 1, 20, 20) Smooth;
+ TextureTrackHover = "Black.png" Part(284, 203, 20, 20) Smooth;
+ TextureThumb = "Black.png" Part(23, 230, 20, 20) Smooth;
+ TextureThumbHover = "Black.png" Part(306, 193, 20, 20) Smooth;
+ TextureArrowUp = "Black.png" Part(284, 225, 20, 20) Middle(0, 0, 20, 19) Smooth;
+ TextureArrowUpHover = "Black.png" Part(285, 171, 20, 20) Middle(0, 0, 20, 19) Smooth;
+ TextureArrowDown = "Black.png" Part(1, 230, 20, 20) Middle(0, 1, 20, 19) Smooth;
+ TextureArrowDownHover = "Black.png" Part(306, 225, 20, 20) Middle(0, 1, 20, 19) Smooth;
+}
+
+Slider {
+ TextureTrack = "Black.png" Part(172, 209, 20, 45) Middle(0, 15) Smooth;
+ TextureTrackHover = "Black.png" Part(194, 209, 20, 45) Middle(0, 15) Smooth;
+ TextureThumb = "Black.png" Part(253, 171, 30, 30) Smooth;
+}
+
+SpinButton {
+ TextureArrowUp = "Black.png" Part(284, 225, 20, 20) Middle(0, 0, 20, 19) Smooth;
+ TextureArrowUpHover = "Black.png" Part(285, 171, 20, 20) Middle(0, 0, 20, 19) Smooth;
+ TextureArrowDown = "Black.png" Part(1, 230, 20, 20) Middle(0, 1, 20, 19) Smooth;
+ TextureArrowDownHover = "Black.png" Part(306, 225, 20, 20) Middle(0, 1, 20, 19) Smooth;
+ BorderBetweenArrows = 0;
+}
+
+Tabs {
+ TextureTab = "Black.png" Part(1, 196, 60, 32) Middle(16, 0) Smooth;
+ TextureSelectedTab = "Black.png" Part(63, 112, 60, 32) Middle(16, 0) Smooth;
+ DistanceToSide = 8;
+}
+
+TextArea {
+ TextureBackground = "Black.png" Part(63, 146, 48, 48) Middle(16) Smooth;
+ CaretColor = rgb(110, 110, 255);
+ Padding = (3, 3, 3, 3);
+}
+
+ToolTip {
+ BackgroundColor = rgb(80, 80, 80);
+ Borders = (1, 1, 1, 1);
+ Padding = (2, 2, 2, 2);
+}
+
+ToggleButton {
+ Texture = "Black.png" Part(140, 53, 45, 50) Middle(10, 0) Smooth;
+ TextureDown = "Black.png" Part(125, 105, 45, 50) Middle(10, 0) Smooth;
+}
\ No newline at end of file
diff --git a/Resources/Pictures/pause.png b/Resources/Pictures/pause.png
new file mode 100644
index 0000000..a149af9
Binary files /dev/null and b/Resources/Pictures/pause.png differ
diff --git a/Resources/Pictures/play.png b/Resources/Pictures/play.png
new file mode 100644
index 0000000..9846509
Binary files /dev/null and b/Resources/Pictures/play.png differ
diff --git a/biosim4.ini b/biosim4.ini
index f57ac38..9de0cbd 100644
--- a/biosim4.ini
+++ b/biosim4.ini
@@ -17,7 +17,7 @@
# numThreads must be 1 or greater. Best value is less than or equal to
# the number of CPU cores. Cannot be changed after a simulation starts.
-numThreads = 4
+numThreads = 16
# sizeX, sizeY define the size of the 2D world. Minimum size is 16,16.
# Maximum size is 32767, 32767. Cannot be changed after a simulation starts.
@@ -26,14 +26,14 @@ sizeY = 128
# Population at the start of each generation. Maximum value = 32766.
# Cannot be changed after a simulation starts.
-population = 3000
+population = 1500
# Number of simulation steps per generation. Range 1..INT_MAX.
-stepsPerGeneration = 300
+stepsPerGeneration = 500
# The simulator will stop when the generation number == maxGenerations.
# Range 1..INT_MAX
-maxGenerations = 200000
+maxGenerations = 3000
# genomeInitialLengthMin and genomeInitialLengthMax should be set to
# the same value. (For future use, the max length might be larger to
@@ -41,8 +41,8 @@ maxGenerations = 200000
# must be no larger than genomeMaxLength. The range of genomeMaxLength
# is genomeInitialLengthMax..INT_MAX. Cannot be changed after a
# simulation starts.
-genomeInitialLengthMin = 24
-genomeInitialLengthMax = 24
+genomeInitialLengthMin = 32
+genomeInitialLengthMax = 32
genomeMaxLength = 300
# maxNumberNeurons is the maximum number of internal neurons that may
@@ -60,12 +60,12 @@ killEnable = false
# If sexualReproduction is false, newborns inherit the genes from a
# single parent. If true, newborns inherit a mixture of genes from
# two parents.
-sexualReproduction = true
+sexualReproduction = false
# If chooseParentByFitness is false, then every agent that survives the
# selection criterion has equal chance of reproducing. If true, then
# preference is given to those parents who passed the selection criterion
-# with a greater score. Fitness scores are determined in survival-criteria.cpp.
+# with a greater score. Fitness scores are determined in survivalCriteria.cpp.
chooseParentsByFitness = true
# pointMutationRate is the probability per gene of having a single-bit
@@ -111,11 +111,11 @@ signalLayers = 1
# imageDir is the relative or absolute directory path where generation
# movies are created.
-imageDir = images
+imageDir = Output/Videos
# logDir is the relative or absolute directory path where text log files
# are created.
-logDir = logs
+logDir = Output/Logs
# displayScale scales the generation movie. Typical values are
# 1 for actual size, or 2, 4, 8, 16, or 32 to scale up the movie.
@@ -146,7 +146,7 @@ saveVideo = true
# videoStride determines how often generation movies will be created.
# Also see saveVideo and videoSaveFirstFrames. Range 1..INT_MAX.
-videoStride = 25
+videoStride = 1
# updateGraphLogStride determines how often the simulation progress graph
# is updated by direct invocation of graphlog.gp. Ignored if updateGraphLog
@@ -196,7 +196,8 @@ displaySampleGenomes = 5
# 15 = pairs
# 16 = contact location sequence
# 17 = altruism, circle + NE corner
-challenge = 6
+# 19 = challenge corner random
+challenge = 19
# The simulator supports a feature called "barriers." Barriers are locations
# in the simulated 2D world where agents may not occupy. The value of
@@ -209,7 +210,7 @@ challenge = 6
# 4 = horiz bar constant location north center
# 5 = floating islands
# 6 = sequence of spots
-barrierType = 0
+barrierType = 3
# This is an example of an automatic parameter change based on the generation.
# If uncommented, the barrier type will automatically change to the new value
@@ -227,3 +228,4 @@ deterministic = false
# are integers 0 to 4294967295. Cannot be changed after a simulation starts.
RNGSeed = 12345678
+autoSave = false
\ No newline at end of file
diff --git a/biosim4.layout b/biosim4.layout
new file mode 100644
index 0000000..a46aa83
--- /dev/null
+++ b/biosim4.layout
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/screenshot.png b/screenshot.png
new file mode 100644
index 0000000..29d765c
Binary files /dev/null and b/screenshot.png differ
diff --git a/src/executeActions.cpp b/src/ai/executeActions.cpp
similarity index 98%
rename from src/executeActions.cpp
rename to src/ai/executeActions.cpp
index 2718d38..b928653 100644
--- a/src/executeActions.cpp
+++ b/src/ai/executeActions.cpp
@@ -5,11 +5,14 @@
#include
#include
#include
-#include "simulator.h"
+#include "../simulator.h"
+#include "../utils/random.h"
#include "omp.h"
namespace BS {
+extern RandomUintGenerator randomUint;
+
// Given a factor in the range 0.0..1.0, return a bool with the
// probability of it being true proportional to factor. For example, if
// factor == 0.2, then there is a 20% chance this function will
@@ -61,7 +64,7 @@ strong level, we do this:
peeps.queueForDeath()
The deferred movement and death queues will be emptied by the caller at the end of the
-simulator step by endOfSimStep() in a single thread after all individuals have been
+simulator step by endOfStep() in a single thread after all individuals have been
evaluated multithreadedly.
**********************************************************************************/
diff --git a/src/feedForward.cpp b/src/ai/feedForward.cpp
similarity index 99%
rename from src/feedForward.cpp
rename to src/ai/feedForward.cpp
index 2ad53dc..30e8d82 100644
--- a/src/feedForward.cpp
+++ b/src/ai/feedForward.cpp
@@ -3,7 +3,8 @@
#include
#include
#include
-#include "simulator.h"
+#include
+#include "../simulator.h"
namespace BS {
diff --git a/src/genome-compare.cpp b/src/ai/genome-compare.cpp
similarity index 93%
rename from src/genome-compare.cpp
rename to src/ai/genome-compare.cpp
index da2b93c..6b17ae0 100644
--- a/src/genome-compare.cpp
+++ b/src/ai/genome-compare.cpp
@@ -1,10 +1,14 @@
// genome-compare.cpp -- compute similarity of two genomes
#include
-#include "simulator.h"
+#include "../simulator.h"
+#include "genome-neurons.h"
+#include "../peeps.h"
namespace BS {
+extern Peeps peeps;
+
// Approximate gene match: Has to match same source, sink, with similar weight
//
bool genesMatch(const Gene &g1, const Gene &g2)
@@ -152,17 +156,17 @@ float genomeSimilarity(const Genome &g1, const Genome &g2)
// Samples random pairs of individuals regardless if they are alive or not
float geneticDiversity()
{
- if (p.population < 2) {
+ if (peeps.getPopulation() < 2) {
return 0.0;
}
// count limits the number of genomes sampled for performance reasons.
- unsigned count = std::min(1000U, p.population); // todo: !!! p.analysisSampleSize;
+ unsigned count = std::min(1000U, peeps.getPopulation()); // todo: !!! p.analysisSampleSize;
int numSamples = 0;
float similaritySum = 0.0f;
while (count > 0) {
- unsigned index0 = randomUint(1, p.population - 1); // skip first and last elements
+ unsigned index0 = randomUint(1, peeps.getPopulation() - 1); // skip first and last elements
unsigned index1 = index0 + 1;
similaritySum += genomeSimilarity(peeps[index0].genome, peeps[index1].genome);
--count;
diff --git a/src/genome-neurons.h b/src/ai/genome-neurons.h
similarity index 85%
rename from src/genome-neurons.h
rename to src/ai/genome-neurons.h
index 185a579..75b1b10 100644
--- a/src/genome-neurons.h
+++ b/src/ai/genome-neurons.h
@@ -4,8 +4,9 @@
#include
#include
#include
-#include "sensors-actions.h"
-#include "random.h"
+#include "./sensors-actions.h"
+#include "../utils/random.h"
+
namespace BS {
@@ -32,6 +33,25 @@ struct Gene { //__attribute__((packed)) Gene {
//float weightAsFloat() { return std::pow(weight / f1, 3.0) / f2; }
float weightAsFloat() const { return weight / 8192.0; }
static int16_t makeRandomWeight() { return randomUint(0, 0xffff) - 0x8000; }
+
+ template
+ void save(Archive &ar) const
+ {
+ ar(uint16_t(sourceType), uint16_t(sourceNum), uint16_t(sinkType), uint16_t(sinkNum), int16_t(weight));
+ }
+
+ template
+ void load(Archive &ar)
+ {
+ uint16_t sourceType_l, sourceNum_l, sinkType_l, sinkNum_l;
+ int16_t weight_l;
+ ar(sourceType_l, sourceNum_l, sinkType_l, sinkNum_l, weight_l);
+ sourceType = sourceType_l;
+ sourceNum = sourceNum_l;
+ sinkType = sinkType_l;
+ sinkNum = sinkNum_l;
+ weight = weight_l;
+ }
};
diff --git a/src/genome.cpp b/src/ai/genome.cpp
similarity index 99%
rename from src/genome.cpp
rename to src/ai/genome.cpp
index 6495d26..308d811 100644
--- a/src/genome.cpp
+++ b/src/ai/genome.cpp
@@ -6,8 +6,10 @@
#include
#include
#include
-#include "simulator.h"
-#include "random.h"
+#include "../simulator.h"
+#include "../utils/random.h"
+#include "genome-neurons.h"
+#include "./indiv.h"
namespace BS {
@@ -366,7 +368,7 @@ Genome generateChildGenome(const std::vector &parentGenomes)
// all the candidate parents with equal preference. If the parameter is
// true, then we give preference to candidate parents according to their
// score. Their score was computed by the survival/selection algorithm
- // in survival-criteria.cpp.
+ // in survivalСriteria.cpp.
if (p.chooseParentsByFitness && parentGenomes.size() > 1) {
parent1Idx = randomUint(1, parentGenomes.size() - 1);
parent2Idx = randomUint(0, parent1Idx - 1);
diff --git a/src/getSensor.cpp b/src/ai/getSensor.cpp
similarity index 98%
rename from src/getSensor.cpp
rename to src/ai/getSensor.cpp
index b74f34c..4a0a5c5 100644
--- a/src/getSensor.cpp
+++ b/src/ai/getSensor.cpp
@@ -3,10 +3,19 @@
#include
#include
#include
-#include "simulator.h"
+#include "../simulator.h"
+#include "../basicTypes.h"
+#include "../grid.h"
+#include "./signals.h"
+#include "./indiv.h"
+#include "../peeps.h"
namespace BS {
+extern Grid grid;
+extern Signals signals;
+extern Peeps peeps;
+
float getPopulationDensityAlongAxis(Coord loc, Dir dir)
{
// Converts the population along the specified axis to the sensor range. The
diff --git a/src/ai/indiv.cpp b/src/ai/indiv.cpp
new file mode 100644
index 0000000..948c803
--- /dev/null
+++ b/src/ai/indiv.cpp
@@ -0,0 +1,138 @@
+// indiv.cpp
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "../simulator.h"
+#include "indiv.h"
+#include "../grid.h"
+
+namespace BS {
+
+extern std::string sensorShortName(Sensor sensor);
+extern std::string actionShortName(Action action);
+extern Grid grid;
+
+// This is called when any individual is spawned.
+// The responsiveness parameter will be initialized here to maximum value
+// of 1.0, then depending on which action activation function is used,
+// the default undriven value may be changed to 1.0 or action midrange.
+void Indiv::initialize(uint16_t index_, Coord loc_, Genome &&genome_)
+{
+ index = index_;
+ loc = loc_;
+ birthLoc = Coord(loc.x, loc.y);
+ genome = std::move(genome_);
+ this->initVariables();
+}
+
+void Indiv::initVariables()
+{
+ grid.set(this->loc, this->index);
+
+ age = 0;
+ oscPeriod = 34; // ToDo !!! define a constant
+ alive = true;
+ lastMoveDir = Dir::random8();
+ responsiveness = 0.5; // range 0.0..1.0
+ longProbeDist = p.longProbeDistance;
+ challengeBits = (unsigned)false; // will be set true when some task gets accomplished
+ createWiringFromGenome();
+
+ this->shape.setRadius(2);
+ this->fillColor();
+}
+
+/**
+ * Generates a genetic color based on the properties of the Indiv's genome.
+ *
+ * @return A uint8_t representing the genetic color.
+ */
+uint8_t Indiv::makeGeneticColor()
+{
+ return ((this->genome.size() & 1)
+ | ((this->genome.front().sourceType) << 1)
+ | ((this->genome.back().sourceType) << 2)
+ | ((this->genome.front().sinkType) << 3)
+ | ((this->genome.back().sinkType) << 4)
+ | ((this->genome.front().sourceNum & 1) << 5)
+ | ((this->genome.front().sinkNum & 1) << 6)
+ | ((this->genome.back().sourceNum & 1) << 7));
+}
+
+void Indiv::fillColor()
+{
+ uint8_t rawColor = this->makeGeneticColor();
+ uint8_t color[3];
+
+ constexpr uint8_t minColorVal = 100;
+ constexpr uint8_t minLumaVal = 50;
+ auto rgbToLuma = [](uint8_t r, uint8_t g, uint8_t b) { return (r+r+r+b+g+g+g+g) / 8; };
+
+ color[0] = (rawColor); // R: 0..255
+ color[1] = ((rawColor & 0x1f) << 3); // G: 0..255 & 00011111 << 3
+ color[2] = ((rawColor & 7) << 5); // B: 0..255 & 00000111 << 5
+
+ // Prevent color mappings to very bright colors (hard to see):
+ if (rgbToLuma(color[0], color[1], color[2]) < minLumaVal) {
+ if (color[0] < minColorVal) color[0] = 255 - color[0];
+ if (color[1] < minColorVal) color[1] = 255 - color[1];
+ if (color[2] < minColorVal) color[2] = 255 - color[2];
+ }
+
+ this->shape.setFillColor(sf::Color(color[0], color[1], color[2], 255));
+}
+
+// This prints a neural net in a form that can be processed with
+// graph-nnet.py to produce a graphic illustration of the net.
+std::stringstream Indiv::printIGraphEdgeList() const
+{
+ std::stringstream ss;
+ for (auto & conn : nnet.connections) {
+ if (conn.sourceType == SENSOR) {
+ ss << sensorShortName((Sensor)(conn.sourceNum));
+ } else {
+ ss << "N" << std::to_string(conn.sourceNum);
+ }
+
+ ss << " ";
+
+ if (conn.sinkType == ACTION) {
+ ss << actionShortName((Action)(conn.sinkNum));
+ } else {
+ ss << "N" << std::to_string(conn.sinkNum);
+ }
+
+ ss << " " << std::to_string(conn.weight) << std::endl;
+ }
+ return ss;
+}
+
+
+// Format: 32-bit hex strings, one per gene
+void Indiv::printGenome() const
+{
+ constexpr unsigned genesPerLine = 8;
+ unsigned count = 0;
+ for (Gene gene : genome) {
+ if (count == genesPerLine) {
+ std::cout << std::endl;
+ count = 0;
+ } else if (count != 0) {
+ std::cout << " ";
+ }
+
+ assert(sizeof(Gene) == 4);
+ uint32_t n;
+ std::memcpy(&n, &gene, sizeof(n));
+ std::cout << std::hex << std::setfill('0') << std::setw(8) << n;
+ ++count;
+ }
+ std::cout << std::dec << std::endl;
+}
+
+} // end namespace BS
diff --git a/src/indiv.h b/src/ai/indiv.h
similarity index 70%
rename from src/indiv.h
rename to src/ai/indiv.h
index f7c1d81..473b05c 100644
--- a/src/indiv.h
+++ b/src/ai/indiv.h
@@ -3,11 +3,15 @@
// Indiv is the structure that represents one individual agent.
+#include
+
#include
#include
#include
-#include "basicTypes.h"
-#include "genome-neurons.h"
+#include "../basicTypes.h"
+#include "./genome-neurons.h"
+#include
+#include
namespace BS {
@@ -30,9 +34,20 @@ struct Indiv {
float getSensor(Sensor, unsigned simStep) const;
void initialize(uint16_t index, Coord loc, Genome &&genome);
void createWiringFromGenome(); // creates .nnet member from .genome member
- void printNeuralNet() const;
- void printIGraphEdgeList() const;
+ std::stringstream printNeuralNet() const;
+ std::stringstream printIGraphEdgeList() const;
void printGenome() const;
+
+ sf::CircleShape shape;
+ uint8_t makeGeneticColor();
+ void fillColor();
+ void initVariables();
+
+ template
+ void serialize(Archive &ar)
+ {
+ ar(index, CEREAL_NVP(loc), CEREAL_NVP(genome));
+ }
};
} // end namespace BS
diff --git a/src/sensors-actions.h b/src/ai/sensors-actions.h
similarity index 99%
rename from src/sensors-actions.h
rename to src/ai/sensors-actions.h
index 30b768a..3cf4d5d 100644
--- a/src/sensors-actions.h
+++ b/src/ai/sensors-actions.h
@@ -9,6 +9,7 @@
namespace BS {
+
// Neuron Sources (Sensors) and Sinks (Actions)
// These sensor, neuron, and action value ranges are here for documentation
@@ -82,8 +83,8 @@ enum Action {
MOVE_LEFT, // W
MOVE_RIGHT, // W
MOVE_REVERSE, // W
- NUM_ACTIONS, // <<----------------- END OF ACTIVE ACTIONS MARKER
KILL_FORWARD, // W
+ NUM_ACTIONS, // <<----------------- END OF ACTIVE ACTIONS MARKER
};
extern std::string sensorName(Sensor sensor);
diff --git a/src/signals.cpp b/src/ai/signals.cpp
similarity index 97%
rename from src/signals.cpp
rename to src/ai/signals.cpp
index 9de92bc..9a93b16 100644
--- a/src/signals.cpp
+++ b/src/ai/signals.cpp
@@ -2,7 +2,8 @@
// Manages layers of pheremones
#include
-#include "simulator.h"
+#include "signals.h"
+#include "../simulator.h"
namespace BS {
diff --git a/src/signals.h b/src/ai/signals.h
similarity index 98%
rename from src/signals.h
rename to src/ai/signals.h
index a096f50..b4453cc 100644
--- a/src/signals.h
+++ b/src/ai/signals.h
@@ -5,7 +5,7 @@
#include
#include
-#include "basicTypes.h"
+#include "../basicTypes.h"
namespace BS {
diff --git a/src/analysis.cpp b/src/analysis.cpp
deleted file mode 100644
index cc8f7e6..0000000
--- a/src/analysis.cpp
+++ /dev/null
@@ -1,375 +0,0 @@
-// analysis.cpp -- various reports
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include "simulator.h"
-
-namespace BS {
-
-// This converts sensor numbers to descriptive strings.
-std::string sensorName(Sensor sensor)
-{
- switch(sensor) {
- case AGE: return "age"; break;
- case BOUNDARY_DIST: return "boundary dist"; break;
- case BOUNDARY_DIST_X: return "boundary dist X"; break;
- case BOUNDARY_DIST_Y: return "boundary dist Y"; break;
- case LAST_MOVE_DIR_X: return "last move dir X"; break;
- case LAST_MOVE_DIR_Y: return "last move dir Y"; break;
- case LOC_X: return "loc X"; break;
- case LOC_Y: return "loc Y"; break;
- case LONGPROBE_POP_FWD: return "long probe population fwd"; break;
- case LONGPROBE_BAR_FWD: return "long probe barrier fwd"; break;
- case BARRIER_FWD: return "short probe barrier fwd-rev"; break;
- case BARRIER_LR: return "short probe barrier left-right"; break;
- case OSC1: return "osc1"; break;
- case POPULATION: return "population"; break;
- case POPULATION_FWD: return "population fwd"; break;
- case POPULATION_LR: return "population LR"; break;
- case RANDOM: return "random"; break;
- case SIGNAL0: return "signal 0"; break;
- case SIGNAL0_FWD: return "signal 0 fwd"; break;
- case SIGNAL0_LR: return "signal 0 LR"; break;
- case GENETIC_SIM_FWD: return "genetic similarity fwd"; break;
- default: assert(false); break;
- }
-}
-
-
-// Converts action numbers to descriptive strings.
-std::string actionName(Action action)
-{
- switch(action) {
- case MOVE_EAST: return "move east"; break;
- case MOVE_WEST: return "move west"; break;
- case MOVE_NORTH: return "move north"; break;
- case MOVE_SOUTH: return "move south"; break;
- case MOVE_FORWARD: return "move fwd"; break;
- case MOVE_X: return "move X"; break;
- case MOVE_Y: return "move Y"; break;
- case SET_RESPONSIVENESS: return "set inv-responsiveness"; break;
- case SET_OSCILLATOR_PERIOD: return "set osc1"; break;
- case EMIT_SIGNAL0: return "emit signal 0"; break;
- case KILL_FORWARD: return "kill fwd"; break;
- case MOVE_REVERSE: return "move reverse"; break;
- case MOVE_LEFT: return "move left"; break;
- case MOVE_RIGHT: return "move right"; break;
- case MOVE_RL: return "move R-L"; break;
- case MOVE_RANDOM: return "move random"; break;
- case SET_LONGPROBE_DIST: return "set longprobe dist"; break;
- default: assert(false); break;
- }
-}
-
-
-// This converts sensor numbers to mnemonic strings.
-// Useful for later processing by graph-nnet.py.
-std::string sensorShortName(Sensor sensor)
-{
- switch(sensor) {
- case AGE: return "Age"; break;
- case BOUNDARY_DIST: return "ED"; break;
- case BOUNDARY_DIST_X: return "EDx"; break;
- case BOUNDARY_DIST_Y: return "EDy"; break;
- case LAST_MOVE_DIR_X: return "LMx"; break;
- case LAST_MOVE_DIR_Y: return "LMy"; break;
- case LOC_X: return "Lx"; break;
- case LOC_Y: return "Ly"; break;
- case LONGPROBE_POP_FWD: return "LPf"; break;
- case LONGPROBE_BAR_FWD: return "LPb"; break;
- case BARRIER_FWD: return "Bfd"; break;
- case BARRIER_LR: return "Blr"; break;
- case OSC1: return "Osc"; break;
- case POPULATION: return "Pop"; break;
- case POPULATION_FWD: return "Pfd"; break;
- case POPULATION_LR: return "Plr"; break;
- case RANDOM: return "Rnd"; break;
- case SIGNAL0: return "Sg"; break;
- case SIGNAL0_FWD: return "Sfd"; break;
- case SIGNAL0_LR: return "Slr"; break;
- case GENETIC_SIM_FWD: return "Gen"; break;
- default: assert(false); break;
- }
-}
-
-
-// Converts action numbers to mnemonic strings.
-// Useful for later processing by graph-nnet.py.
-std::string actionShortName(Action action)
-{
- switch(action) {
- case MOVE_EAST: return "MvE"; break;
- case MOVE_WEST: return "MvW"; break;
- case MOVE_NORTH: return "MvN"; break;
- case MOVE_SOUTH: return "MvS"; break;
- case MOVE_X: return "MvX"; break;
- case MOVE_Y: return "MvY"; break;
- case MOVE_FORWARD: return "Mfd"; break;
- case SET_RESPONSIVENESS: return "Res"; break;
- case SET_OSCILLATOR_PERIOD: return "OSC"; break;
- case EMIT_SIGNAL0: return "SG"; break;
- case KILL_FORWARD: return "Klf"; break;
- case MOVE_REVERSE: return "Mrv"; break;
- case MOVE_LEFT: return "MvL"; break;
- case MOVE_RIGHT: return "MvR"; break;
- case MOVE_RL: return "MRL"; break;
- case MOVE_RANDOM: return "Mrn"; break;
- case SET_LONGPROBE_DIST: return "LPD"; break;
- default: assert(false); break;
- }
-}
-
-
-// List the names of the active sensors and actions to stdout.
-// "Active" means those sensors and actions that are compiled into
-// the code. See sensors-actions.h for how to define the enums.
-void printSensorsActions()
-{
- unsigned i;
- std::cout << "Sensors:" << std::endl;
- for (i = 0; i < Sensor::NUM_SENSES; ++i) {
- std::cout << " " << sensorName((Sensor)i) << std::endl;
- }
- std::cout << "Actions:" << std::endl;
- for (i = 0; i < Action::NUM_ACTIONS; ++i) {
- std::cout << " " << actionName((Action)i) << std::endl;
- }
- std::cout << std::endl;
-}
-
-
-// Format: 32-bit hex strings, one per gene
-void Indiv::printGenome() const
-{
- constexpr unsigned genesPerLine = 8;
- unsigned count = 0;
- for (Gene gene : genome) {
- if (count == genesPerLine) {
- std::cout << std::endl;
- count = 0;
- } else if (count != 0) {
- std::cout << " ";
- }
-
- assert(sizeof(Gene) == 4);
- uint32_t n;
- std::memcpy(&n, &gene, sizeof(n));
- std::cout << std::hex << std::setfill('0') << std::setw(8) << n;
- ++count;
- }
- std::cout << std::dec << std::endl;
-}
-
-
-///*
-//Example format:
-//
-// ACTION_NAMEa from:
-// ACTION_NAMEb from:
-// SENSOR i
-// SENSOR j
-// NEURON n
-// NEURON m
-// Neuron x from:
-// SENSOR i
-// SENSOR j
-// NEURON n
-// NEURON m
-// Neuron y ...
-//*/
-//void Indiv::printNeuralNet() const
-//{
-// for (unsigned action = 0; action < Action::NUM_ACTIONS; ++action) {
-// bool actionDisplayed = false;
-// for (auto & conn : nnet.connections) {
-//
-// assert((conn.sourceType == NEURON && conn.sourceNum < p.maxNumberNeurons)
-// || (conn.sourceType == SENSOR && conn.sourceNum < Sensor::NUM_SENSES));
-// assert((conn.sinkType == ACTION && conn.sinkNum < Action::NUM_ACTIONS)
-// || (conn.sinkType == NEURON && conn.sinkNum < p.maxNumberNeurons));
-//
-// if (conn.sinkType == ACTION && (conn.sinkNum) == action) {
-// if (!actionDisplayed) {
-// std::cout << "Action " << actionName((Action)action) << " from:" << std::endl;
-// actionDisplayed = true;
-// }
-// if (conn.sourceType == SENSOR) {
-// std::cout << " " << sensorName((Sensor)(conn.sourceNum));
-// } else {
-// std::cout << " Neuron " << (conn.sourceNum % nnet.neurons.size());
-// }
-// std::cout << " " << conn.weightAsFloat() << std::endl;
-// }
-// }
-// }
-//
-// for (size_t neuronNum = 0; neuronNum < nnet.neurons.size(); ++neuronNum) {
-// bool neuronDisplayed = false;
-// for (auto & conn : nnet.connections) {
-// if (conn.sinkType == NEURON && (conn.sinkNum) == neuronNum) {
-// if (!neuronDisplayed) {
-// std::cout << "Neuron " << neuronNum << " from:" << std::endl;
-// neuronDisplayed = true;
-// }
-// if (conn.sourceType == SENSOR) {
-// std::cout << " " << sensorName((Sensor)(conn.sourceNum));
-// } else {
-// std::cout << " Neuron " << (conn.sourceNum);
-// }
-// std::cout << " " << conn.weightAsFloat() << std::endl;
-// }
-// }
-// }
-//}
-//
-
-
-// This prints a neural net in a form that can be processed with
-// graph-nnet.py to produce a graphic illustration of the net.
-void Indiv::printIGraphEdgeList() const
-{
- for (auto & conn : nnet.connections) {
- if (conn.sourceType == SENSOR) {
- std::cout << sensorShortName((Sensor)(conn.sourceNum));
- } else {
- std::cout << "N" << std::to_string(conn.sourceNum);
- }
-
- std::cout << " ";
-
- if (conn.sinkType == ACTION) {
- std::cout << actionShortName((Action)(conn.sinkNum));
- } else {
- std::cout << "N" << std::to_string(conn.sinkNum);
- }
-
- std::cout << " " << std::to_string(conn.weight) << std::endl;
- }
-}
-
-
-float averageGenomeLength()
-{
- unsigned count = 100;
- unsigned numberSamples = 0;
- unsigned long sum = 0;
-
- while (count-- > 0) {
- sum += peeps[randomUint(1, p.population)].genome.size();
- ++numberSamples;
- }
- return sum / numberSamples;
-}
-
-
-// The epoch log contains one line per generation in a format that can be
-// fed to graphlog.gp to produce a chart of the simulation progress.
-// ToDo: remove hardcoded filename.
-void appendEpochLog(unsigned generation, unsigned numberSurvivors, unsigned murderCount)
-{
- std::ofstream foutput;
-
- if (generation == 0) {
- foutput.open(p.logDir + "/epoch-log.txt");
- foutput.close();
- }
-
- foutput.open(p.logDir + "/epoch-log.txt", std::ios::app);
-
- if (foutput.is_open()) {
- foutput << generation << " " << numberSurvivors << " " << geneticDiversity()
- << " " << averageGenomeLength() << " " << murderCount << std::endl;
- } else {
- assert(false);
- }
-}
-
-
-// Print stats about pheromone usage.
-void displaySignalUse()
-{
- if (Sensor::SIGNAL0 > Sensor::NUM_SENSES && Sensor::SIGNAL0_FWD > Sensor::NUM_SENSES && Sensor::SIGNAL0_LR > Sensor::NUM_SENSES) {
- return;
- }
-
- unsigned long long sum = 0;
- unsigned count = 0;
-
- for (int16_t x = 0; x < p.sizeX; ++x) {
- for (int16_t y = 0; y < p.sizeY; ++y) {
- unsigned magnitude = signals.getMagnitude(0, { x, y });
- if (magnitude != 0) {
- ++count;
- sum += magnitude;
- }
- }
- }
- std::cout << "Signal spread " << ((double)count / (p.sizeX * p.sizeY)) << "%, average " << ((double)sum / (p.sizeX * p.sizeY)) << std::endl;
-}
-
-
-// Print how many connections occur from each kind of sensor neuron and to
-// each kind of action neuron over the entire population. This helps us to
-// see which sensors and actions are most useful for survival.
-void displaySensorActionReferenceCounts()
-{
- std::vector sensorCounts(Sensor::NUM_SENSES, 0);
- std::vector actionCounts(Action::NUM_ACTIONS, 0);
-
- for (unsigned index = 1; index <= p.population; ++index) {
- if (peeps[index].alive) {
- const Indiv &indiv = peeps[index];
- for (const Gene &gene : indiv.nnet.connections) {
- if (gene.sourceType == SENSOR) {
- assert(gene.sourceNum < Sensor::NUM_SENSES);
- ++sensorCounts[(Sensor)gene.sourceNum];
- }
- if (gene.sinkType == ACTION) {
- assert(gene.sinkNum < Action::NUM_ACTIONS);
- ++actionCounts[(Action)gene.sinkNum];
- }
- }
- }
- }
-
- std::cout << "Sensors in use:" << std::endl;
- for (unsigned i = 0; i < sensorCounts.size(); ++i) {
- if (sensorCounts[i] > 0) {
- std::cout << " " << sensorCounts[i] << " - " << sensorName((Sensor)i) << std::endl;
- }
- }
- std::cout << "Actions in use:" << std::endl;
- for (unsigned i = 0; i < actionCounts.size(); ++i) {
- if (actionCounts[i] > 0) {
- std::cout << " " << actionCounts[i] << " - " << actionName((Action)i) << std::endl;
- }
- }
-}
-
-
-void displaySampleGenomes(unsigned count)
-{
- unsigned index = 1; // indexes start at 1
- for (index = 1; count > 0 && index <= p.population; ++index) {
- if (peeps[index].alive) {
- std::cout << "---------------------------\nIndividual ID " << index << std::endl;
- peeps[index].printGenome();
- std::cout << std::endl;
-
- //peeps[index].printNeuralNet();
- peeps[index].printIGraphEdgeList();
-
-
- std::cout << "---------------------------" << std::endl;
- --count;
- }
- }
-
- displaySensorActionReferenceCounts();
-}
-
-} // end namespace BS
diff --git a/src/basicTypes.h b/src/basicTypes.h
index 1b7577f..a77f649 100644
--- a/src/basicTypes.h
+++ b/src/basicTypes.h
@@ -52,7 +52,8 @@ Arithmetic
#include
#include
#include
-#include "random.h"
+#include "./utils/random.h"
+#include
namespace BS {
@@ -112,6 +113,25 @@ struct __attribute__((packed)) Coord {
public:
int16_t x;
int16_t y;
+
+ friend class cereal::access;
+
+ template
+ void save(Archive &ar) const
+ {
+ ar(uint16_t(x), uint16_t(y));
+ }
+
+ template
+ void load(Archive &ar)
+ {
+ uint16_t x_a;
+ uint16_t y_a;
+ ar(x_a, y_a);
+
+ this->x = x_a;
+ this->y = y_a;
+ }
};
diff --git a/src/createBarrier.cpp b/src/createBarrier.cpp
index 10aeac6..821d86e 100644
--- a/src/createBarrier.cpp
+++ b/src/createBarrier.cpp
@@ -25,6 +25,7 @@ void Grid::createBarrier(unsigned barrierType)
for (int16_t y = minY; y <= maxY; ++y) {
grid.set(x, y, BARRIER);
barrierLocations.push_back( {x, y} );
+ barrierCenters.push_back( {x, y} );
}
}
};
@@ -35,18 +36,13 @@ void Grid::createBarrier(unsigned barrierType)
// Vertical bar in constant location
case 1:
- {
+ {
int16_t minX = p.sizeX / 2;
int16_t maxX = minX + 1;
int16_t minY = p.sizeY / 4;
int16_t maxY = minY + p.sizeY / 2;
- for (int16_t x = minX; x <= maxX; ++x) {
- for (int16_t y = minY; y <= maxY; ++y) {
- grid.set(x, y, BARRIER);
- barrierLocations.push_back( {x, y} );
- }
- }
+ drawBox(minX, minY, maxX, maxY);
}
break;
@@ -58,12 +54,7 @@ void Grid::createBarrier(unsigned barrierType)
int16_t minY = randomUint(20, p.sizeY / 2 - 20);
int16_t maxY = minY + p.sizeY / 2;
- for (int16_t x = minX; x <= maxX; ++x) {
- for (int16_t y = minY; y <= maxY; ++y) {
- grid.set(x, y, BARRIER);
- barrierLocations.push_back( {x, y} );
- }
- }
+ drawBox(minX, minY, maxX, maxY);
}
break;
@@ -105,12 +96,7 @@ void Grid::createBarrier(unsigned barrierType)
int16_t minY = p.sizeY / 2 + p.sizeY / 4;
int16_t maxY = minY + 2;
- for (int16_t x = minX; x <= maxX; ++x) {
- for (int16_t y = minY; y <= maxY; ++y) {
- grid.set(x, y, BARRIER);
- barrierLocations.push_back( {x, y} );
- }
- }
+ drawBox(minX, minY, maxX, maxY);
}
break;
@@ -139,18 +125,15 @@ void Grid::createBarrier(unsigned barrierType)
center2 = randomLoc();
} while ((center0 - center2).length() < margin || (center1 - center2).length() < margin);
- barrierCenters.push_back(center0);
- //barrierCenters.push_back(center1);
- //barrierCenters.push_back(center2);
-
auto f = [&](Coord loc) {
grid.set(loc, BARRIER);
barrierLocations.push_back(loc);
+ barrierCenters.push_back(loc);
};
visitNeighborhood(center0, radius, f);
- //visitNeighborhood(center1, radius, f);
- //visitNeighborhood(center2, radius, f);
+ visitNeighborhood(center1, radius, f);
+ visitNeighborhood(center2, radius, f);
}
break;
diff --git a/src/endOfGeneration.cpp b/src/endOfGeneration.cpp
deleted file mode 100644
index 070eb89..0000000
--- a/src/endOfGeneration.cpp
+++ /dev/null
@@ -1,36 +0,0 @@
-// endOfGeneration.cpp
-
-#include
-#include
-#include
-#include
-#include
-#include "simulator.h"
-#include "imageWriter.h"
-
-namespace BS {
-
-// At the end of each generation, we save a video file (if p.saveVideo is true) and
-// print some genomic statistics to stdout (if p.updateGraphLog is true).
-
-void endOfGeneration(unsigned generation)
-{
- {
- if (p.saveVideo &&
- ((generation % p.videoStride) == 0
- || generation <= p.videoSaveFirstFrames
- || (generation >= p.parameterChangeGenerationNumber
- && generation <= p.parameterChangeGenerationNumber + p.videoSaveFirstFrames))) {
- imageWriter.saveGenerationVideo(generation);
- }
- }
-
- {
- if (p.updateGraphLog && (generation == 1 || ((generation % p.updateGraphLogStride) == 0))) {
-#pragma GCC diagnostic ignored "-Wunused-result"
- std::system(p.graphLogUpdateCommand.c_str());
- }
- }
-}
-
-} // end namespace BS
diff --git a/src/endOfSimStep.cpp b/src/endOfSimStep.cpp
deleted file mode 100644
index 609e7c0..0000000
--- a/src/endOfSimStep.cpp
+++ /dev/null
@@ -1,94 +0,0 @@
-// endOfSimStep.cpp
-
-#include
-#include
-#include "simulator.h"
-#include "imageWriter.h"
-
-namespace BS {
-
-/*
-At the end of each sim step, this function is called in single-thread
-mode to take care of several things:
-
-1. We may kill off some agents if a "radioactive" scenario is in progress.
-2. We may flag some agents as meeting some challenge criteria, if such
- a scenario is in progress.
-3. We then drain the deferred death queue.
-4. We then drain the deferred movement queue.
-5. We fade the signal layer(s) (pheromones).
-6. We save the resulting world condition as a single image frame (if
- p.saveVideo is true).
-*/
-
-void endOfSimStep(unsigned simStep, unsigned generation)
-{
- if (p.challenge == CHALLENGE_RADIOACTIVE_WALLS) {
- // During the first half of the generation, the west wall is radioactive,
- // where X == 0. In the last half of the generation, the east wall is
- // radioactive, where X = the area width - 1. There's an exponential
- // falloff of the danger, falling off to zero at the arena half line.
- int16_t radioactiveX = (simStep < p.stepsPerGeneration / 2) ? 0 : p.sizeX - 1;
-
- for (uint16_t index = 1; index <= p.population; ++index) { // index 0 is reserved
- Indiv &indiv = peeps[index];
- if (indiv.alive) {
- int16_t distanceFromRadioactiveWall = std::abs(indiv.loc.x - radioactiveX);
- if (distanceFromRadioactiveWall < p.sizeX / 2) {
- float chanceOfDeath = 1.0 / distanceFromRadioactiveWall;
- if (randomUint() / (float)RANDOM_UINT_MAX < chanceOfDeath) {
- peeps.queueForDeath(indiv);
- }
- }
- }
- }
- }
-
- // If the individual is touching any wall, we set its challengeFlag to true.
- // At the end of the generation, all those with the flag true will reproduce.
- if (p.challenge == CHALLENGE_TOUCH_ANY_WALL) {
- for (uint16_t index = 1; index <= p.population; ++index) { // index 0 is reserved
- Indiv &indiv = peeps[index];
- if (indiv.loc.x == 0 || indiv.loc.x == p.sizeX - 1
- || indiv.loc.y == 0 || indiv.loc.y == p.sizeY - 1) {
- indiv.challengeBits = true;
- }
- }
- }
-
- // If this challenge is enabled, the individual gets a bit set in their challengeBits
- // member if they are within a specified radius of a barrier center. They have to
- // visit the barriers in sequential order.
- if (p.challenge == CHALLENGE_LOCATION_SEQUENCE) {
- float radius = 9.0;
- for (uint16_t index = 1; index <= p.population; ++index) { // index 0 is reserved
- Indiv &indiv = peeps[index];
- for (unsigned n = 0; n < grid.getBarrierCenters().size(); ++n) {
- unsigned bit = 1 << n;
- if ((indiv.challengeBits & bit) == 0) {
- if ((indiv.loc - grid.getBarrierCenters()[n]).length() <= radius) {
- indiv.challengeBits |= bit;
- }
- break;
- }
- }
- }
- }
-
- peeps.drainDeathQueue();
- peeps.drainMoveQueue();
- signals.fade(0); // takes layerNum todo!!!
-
- // saveVideoFrameSync() is the synchronous version of saveVideFrame()
- if (p.saveVideo &&
- ((generation % p.videoStride) == 0
- || generation <= p.videoSaveFirstFrames
- || (generation >= p.parameterChangeGenerationNumber
- && generation <= p.parameterChangeGenerationNumber + p.videoSaveFirstFrames))) {
- if (!imageWriter.saveVideoFrameSync(simStep, generation)) {
- std::cout << "imageWriter busy" << std::endl;
- }
- }
-}
-
-} // end namespace BS
diff --git a/src/include/cereal/access.hpp b/src/include/cereal/access.hpp
new file mode 100644
index 0000000..87d386c
--- /dev/null
+++ b/src/include/cereal/access.hpp
@@ -0,0 +1,351 @@
+/*! \file access.hpp
+ \brief Access control and default construction */
+/*
+ Copyright (c) 2014, Randolph Voorhies, Shane Grant
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the copyright holder nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
+ DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+#ifndef CEREAL_ACCESS_HPP_
+#define CEREAL_ACCESS_HPP_
+
+#include
+#include
+#include
+#include
+
+#include "cereal/macros.hpp"
+#include "cereal/specialize.hpp"
+#include "cereal/details/helpers.hpp"
+
+namespace cereal
+{
+ // ######################################################################
+ //! A class that allows cereal to load smart pointers to types that have no default constructor
+ /*! If your class does not have a default constructor, cereal will not be able
+ to load any smart pointers to it unless you overload LoadAndConstruct
+ for your class, and provide an appropriate load_and_construct method. You can also
+ choose to define a member static function instead of specializing this class.
+
+ The specialization of LoadAndConstruct must be placed within the cereal namespace:
+
+ @code{.cpp}
+ struct MyType
+ {
+ MyType( int x ); // note: no default ctor
+ int myX;
+
+ // Define a serialize or load/save pair as you normally would
+ template
+ void serialize( Archive & ar )
+ {
+ ar( myX );
+ }
+ };
+
+ // Provide a specialization for LoadAndConstruct for your type
+ namespace cereal
+ {
+ template <> struct LoadAndConstruct
+ {
+ // load_and_construct will be passed the archive that you will be loading
+ // from as well as a construct object which you can use as if it were the
+ // constructor for your type. cereal will handle all memory management for you.
+ template
+ static void load_and_construct( Archive & ar, cereal::construct & construct )
+ {
+ int x;
+ ar( x );
+ construct( x );
+ }
+
+ // if you require versioning, simply add a const std::uint32_t as the final parameter, e.g.:
+ // load_and_construct( Archive & ar, cereal::construct & construct, std::uint32_t const version )
+ };
+ } // end namespace cereal
+ @endcode
+
+ Please note that just as in using external serialization functions, you cannot get
+ access to non-public members of your class by befriending cereal::access. If you
+ have the ability to modify the class you wish to serialize, it is recommended that you
+ use member serialize functions and a static member load_and_construct function.
+
+ load_and_construct functions, regardless of whether they are static members of your class or
+ whether you create one in the LoadAndConstruct specialization, have the following signature:
+
+ @code{.cpp}
+ // generally Archive will be templated, but it can be specific if desired
+ template
+ static void load_and_construct( Archive & ar, cereal::construct & construct );
+ // with an optional last parameter specifying the version: const std::uint32_t version
+ @endcode
+
+ Versioning behaves the same way as it does for standard serialization functions.
+
+ @tparam T The type to specialize for
+ @ingroup Access */
+ template
+ struct LoadAndConstruct
+ { };
+
+ // forward decl for construct
+ //! @cond PRIVATE_NEVERDEFINED
+ namespace memory_detail{ template struct LoadAndConstructLoadWrapper; }
+ namespace boost_variant_detail{ template struct LoadAndConstructLoadWrapper; }
+ //! @endcond
+
+ //! Used to construct types with no default constructor
+ /*! When serializing a type that has no default constructor, cereal
+ will attempt to call either the class static function load_and_construct
+ or the appropriate template specialization of LoadAndConstruct. cereal
+ will pass that function a reference to the archive as well as a reference
+ to a construct object which should be used to perform the allocation once
+ data has been appropriately loaded.
+
+ @code{.cpp}
+ struct MyType
+ {
+ // note the lack of default constructor
+ MyType( int xx, int yy );
+
+ int x, y;
+ double notInConstructor;
+
+ template
+ void serialize( Archive & ar )
+ {
+ ar( x, y );
+ ar( notInConstructor );
+ }
+
+ template
+ static void load_and_construct( Archive & ar, cereal::construct & construct )
+ {
+ int x, y;
+ ar( x, y );
+
+ // use construct object to initialize with loaded data
+ construct( x, y );
+
+ // access to member variables and functions via -> operator
+ ar( construct->notInConstructor );
+
+ // could also do the above section by:
+ double z;
+ ar( z );
+ construct->notInConstructor = z;
+ }
+ };
+ @endcode
+
+ @tparam T The class type being serialized
+ */
+ template
+ class construct
+ {
+ public:
+ //! Construct and initialize the type T with the given arguments
+ /*! This will forward all arguments to the underlying type T,
+ calling an appropriate constructor.
+
+ Calling this function more than once will result in an exception
+ being thrown.
+
+ @param args The arguments to the constructor for T
+ @throw Exception If called more than once */
+ template
+ void operator()( Args && ... args );
+ // implementation deferred due to reliance on cereal::access
+
+ //! Get a reference to the initialized underlying object
+ /*! This must be called after the object has been initialized.
+
+ @return A reference to the initialized object
+ @throw Exception If called before initialization */
+ T * operator->()
+ {
+ if( !itsValid )
+ throw Exception("Object must be initialized prior to accessing members");
+
+ return itsPtr;
+ }
+
+ //! Returns a raw pointer to the initialized underlying object
+ /*! This is mainly intended for use with passing an instance of
+ a constructed object to cereal::base_class.
+
+ It is strongly recommended to avoid using this function in
+ any other circumstance.
+
+ @return A raw pointer to the initialized type */
+ T * ptr()
+ {
+ return operator->();
+ }
+
+ private:
+ template friend struct ::cereal::memory_detail::LoadAndConstructLoadWrapper;
+ template friend struct ::cereal::boost_variant_detail::LoadAndConstructLoadWrapper;
+
+ construct( T * p ) : itsPtr( p ), itsEnableSharedRestoreFunction( [](){} ), itsValid( false ) {}
+ construct( T * p, std::function enableSharedFunc ) : // g++4.7 ice with default lambda to std func
+ itsPtr( p ), itsEnableSharedRestoreFunction( enableSharedFunc ), itsValid( false ) {}
+ construct( construct const & ) = delete;
+ construct & operator=( construct const & ) = delete;
+
+ T * itsPtr;
+ std::function itsEnableSharedRestoreFunction;
+ bool itsValid;
+ };
+
+ // ######################################################################
+ //! A class that can be made a friend to give cereal access to non public functions
+ /*! If you desire non-public serialization functions within a class, cereal can only
+ access these if you declare cereal::access a friend.
+
+ @code{.cpp}
+ class MyClass
+ {
+ private:
+ friend class cereal::access; // gives access to the private serialize
+
+ template
+ void serialize( Archive & ar )
+ {
+ // some code
+ }
+ };
+ @endcode
+ @ingroup Access */
+ class access
+ {
+ public:
+ // ####### Standard Serialization ########################################
+ template inline
+ static auto member_serialize(Archive & ar, T & t) -> decltype(t.CEREAL_SERIALIZE_FUNCTION_NAME(ar))
+ { return t.CEREAL_SERIALIZE_FUNCTION_NAME(ar); }
+
+ template inline
+ static auto member_save(Archive & ar, T const & t) -> decltype(t.CEREAL_SAVE_FUNCTION_NAME(ar))
+ { return t.CEREAL_SAVE_FUNCTION_NAME(ar); }
+
+ template inline
+ static auto member_save_non_const(Archive & ar, T & t) -> decltype(t.CEREAL_SAVE_FUNCTION_NAME(ar))
+ { return t.CEREAL_SAVE_FUNCTION_NAME(ar); }
+
+ template inline
+ static auto member_load(Archive & ar, T & t) -> decltype(t.CEREAL_LOAD_FUNCTION_NAME(ar))
+ { return t.CEREAL_LOAD_FUNCTION_NAME(ar); }
+
+ template inline
+ static auto member_save_minimal(Archive const & ar, T const & t) -> decltype(t.CEREAL_SAVE_MINIMAL_FUNCTION_NAME(ar))
+ { return t.CEREAL_SAVE_MINIMAL_FUNCTION_NAME(ar); }
+
+ template inline
+ static auto member_save_minimal_non_const(Archive const & ar, T & t) -> decltype(t.CEREAL_SAVE_MINIMAL_FUNCTION_NAME(ar))
+ { return t.CEREAL_SAVE_MINIMAL_FUNCTION_NAME(ar); }
+
+ template inline
+ static auto member_load_minimal(Archive const & ar, T & t, U && u) -> decltype(t.CEREAL_LOAD_MINIMAL_FUNCTION_NAME(ar, std::forward(u)))
+ { return t.CEREAL_LOAD_MINIMAL_FUNCTION_NAME(ar, std::forward(u)); }
+
+ // ####### Versioned Serialization #######################################
+ template inline
+ static auto member_serialize(Archive & ar, T & t, const std::uint32_t version ) -> decltype(t.CEREAL_SERIALIZE_FUNCTION_NAME(ar, version))
+ { return t.CEREAL_SERIALIZE_FUNCTION_NAME(ar, version); }
+
+ template inline
+ static auto member_save(Archive & ar, T const & t, const std::uint32_t version ) -> decltype(t.CEREAL_SAVE_FUNCTION_NAME(ar, version))
+ { return t.CEREAL_SAVE_FUNCTION_NAME(ar, version); }
+
+ template inline
+ static auto member_save_non_const(Archive & ar, T & t, const std::uint32_t version ) -> decltype(t.CEREAL_SAVE_FUNCTION_NAME(ar, version))
+ { return t.CEREAL_SAVE_FUNCTION_NAME(ar, version); }
+
+ template inline
+ static auto member_load(Archive & ar, T & t, const std::uint32_t version ) -> decltype(t.CEREAL_LOAD_FUNCTION_NAME(ar, version))
+ { return t.CEREAL_LOAD_FUNCTION_NAME(ar, version); }
+
+ template inline
+ static auto member_save_minimal(Archive const & ar, T const & t, const std::uint32_t version) -> decltype(t.CEREAL_SAVE_MINIMAL_FUNCTION_NAME(ar, version))
+ { return t.CEREAL_SAVE_MINIMAL_FUNCTION_NAME(ar, version); }
+
+ template inline
+ static auto member_save_minimal_non_const(Archive const & ar, T & t, const std::uint32_t version) -> decltype(t.CEREAL_SAVE_MINIMAL_FUNCTION_NAME(ar, version))
+ { return t.CEREAL_SAVE_MINIMAL_FUNCTION_NAME(ar, version); }
+
+ template inline
+ static auto member_load_minimal(Archive const & ar, T & t, U && u, const std::uint32_t version) -> decltype(t.CEREAL_LOAD_MINIMAL_FUNCTION_NAME(ar, std::forward(u), version))
+ { return t.CEREAL_LOAD_MINIMAL_FUNCTION_NAME(ar, std::forward(u), version); }
+
+ // ####### Other Functionality ##########################################
+ // for detecting inheritance from enable_shared_from_this
+ template inline
+ static auto shared_from_this(T & t) -> decltype(t.shared_from_this());
+
+ // for placement new
+ template inline
+ static void construct( T *& ptr, Args && ... args )
+ {
+ new (ptr) T( std::forward( args )... );
+ }
+
+ // for non-placement new with a default constructor
+ template inline
+ static T * construct()
+ {
+ return new T();
+ }
+
+ template inline
+ static std::false_type load_and_construct(...)
+ { return std::false_type(); }
+
+ template inline
+ static auto load_and_construct(Archive & ar, ::cereal::construct & construct) -> decltype(T::load_and_construct(ar, construct))
+ {
+ T::load_and_construct( ar, construct );
+ }
+
+ template inline
+ static auto load_and_construct(Archive & ar, ::cereal::construct & construct, const std::uint32_t version) -> decltype(T::load_and_construct(ar, construct, version))
+ {
+ T::load_and_construct( ar, construct, version );
+ }
+ }; // end class access
+
+ // ######################################################################
+ // Deferred Implementation, see construct for more information
+ template template inline
+ void construct::operator()( Args && ... args )
+ {
+ if( itsValid )
+ throw Exception("Attempting to construct an already initialized object");
+
+ ::cereal::access::construct( itsPtr, std::forward( args )... );
+ itsEnableSharedRestoreFunction();
+ itsValid = true;
+ }
+} // namespace cereal
+
+#endif // CEREAL_ACCESS_HPP_
diff --git a/src/include/cereal/archives/adapters.hpp b/src/include/cereal/archives/adapters.hpp
new file mode 100644
index 0000000..e2fb57d
--- /dev/null
+++ b/src/include/cereal/archives/adapters.hpp
@@ -0,0 +1,163 @@
+/*! \file adapters.hpp
+ \brief Archive adapters that provide additional functionality
+ on top of an existing archive */
+/*
+ Copyright (c) 2014, Randolph Voorhies, Shane Grant
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the copyright holder nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
+ DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+#ifndef CEREAL_ARCHIVES_ADAPTERS_HPP_
+#define CEREAL_ARCHIVES_ADAPTERS_HPP_
+
+#include "cereal/details/helpers.hpp"
+#include
+
+namespace cereal
+{
+ #ifdef CEREAL_FUTURE_EXPERIMENTAL
+
+ // Forward declaration for friend access
+ template U & get_user_data( A & );
+
+ //! Wraps an archive and gives access to user data
+ /*! This adapter is useful if you require access to
+ either raw pointers or references within your
+ serialization functions.
+
+ While cereal does not directly support serialization
+ raw pointers or references, it is sometimes the case
+ that you may want to supply something such as a raw
+ pointer or global reference to some constructor.
+ In this situation this adapter would likely be used
+ with the construct class to allow for non-default
+ constructors.
+
+ @note This feature is experimental and may be altered or removed in a future release. See issue #46.
+
+ @code{.cpp}
+ struct MyUserData
+ {
+ int * myRawPointer;
+ std::reference_wrapper myReference;
+ };
+
+ struct MyClass
+ {
+ // Note the raw pointer parameter
+ MyClass( int xx, int * rawP );
+
+ int x;
+
+ template
+ void serialize( Archive & ar )
+ { ar( x ); }
+
+ template
+ static void load_and_construct( Archive & ar, cereal::construct & construct )
+ {
+ int xx;
+ ar( xx );
+ // note the need to use get_user_data to retrieve user data from the archive
+ construct( xx, cereal::get_user_data( ar ).myRawPointer );
+ }
+ };
+
+ int main()
+ {
+ {
+ MyUserData md;
+ md.myRawPointer = &something;
+ md.myReference = someInstanceOfType;
+
+ std::ifstream is( "data.xml" );
+ cereal::UserDataAdapter ar( md, is );
+
+ std::unique_ptr sc;
+ ar( sc ); // use as normal
+ }
+
+ return 0;
+ }
+ @endcode
+
+ @relates get_user_data
+
+ @tparam UserData The type to give the archive access to
+ @tparam Archive The archive to wrap */
+ template
+ class UserDataAdapter : public Archive
+ {
+ public:
+ //! Construct the archive with some user data struct
+ /*! This will forward all arguments (other than the user
+ data) to the wrapped archive type. The UserDataAdapter
+ can then be used identically to the wrapped archive type
+
+ @tparam Args The arguments to pass to the constructor of
+ the archive. */
+ template
+ UserDataAdapter( UserData & ud, Args && ... args ) :
+ Archive( std::forward( args )... ),
+ userdata( ud )
+ { }
+
+ private:
+ //! Overload the rtti function to enable dynamic_cast
+ void rtti() {}
+ friend UserData & get_user_data( Archive & ar );
+ UserData & userdata; //!< The actual user data
+ };
+
+ //! Retrieves user data from an archive wrapped by UserDataAdapter
+ /*! This will attempt to retrieve the user data associated with
+ some archive wrapped by UserDataAdapter. If this is used on
+ an archive that is not wrapped, a run-time exception will occur.
+
+ @note This feature is experimental and may be altered or removed in a future release. See issue #46.
+
+ @note The correct use of this function cannot be enforced at compile
+ time.
+
+ @relates UserDataAdapter
+ @tparam UserData The data struct contained in the archive
+ @tparam Archive The archive, which should be wrapped by UserDataAdapter
+ @param ar The archive
+ @throws Exception if the archive this is used upon is not wrapped with
+ UserDataAdapter. */
+ template
+ UserData & get_user_data( Archive & ar )
+ {
+ try
+ {
+ return dynamic_cast &>( ar ).userdata;
+ }
+ catch( std::bad_cast const & )
+ {
+ throw ::cereal::Exception("Attempting to get user data from archive not wrapped in UserDataAdapter");
+ }
+ }
+ #endif // CEREAL_FUTURE_EXPERIMENTAL
+} // namespace cereal
+
+#endif // CEREAL_ARCHIVES_ADAPTERS_HPP_
diff --git a/src/include/cereal/archives/binary.hpp b/src/include/cereal/archives/binary.hpp
new file mode 100644
index 0000000..0bded01
--- /dev/null
+++ b/src/include/cereal/archives/binary.hpp
@@ -0,0 +1,169 @@
+/*! \file binary.hpp
+ \brief Binary input and output archives */
+/*
+ Copyright (c) 2014, Randolph Voorhies, Shane Grant
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the copyright holder nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
+ DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+#ifndef CEREAL_ARCHIVES_BINARY_HPP_
+#define CEREAL_ARCHIVES_BINARY_HPP_
+
+#include "cereal/cereal.hpp"
+#include
+
+namespace cereal
+{
+ // ######################################################################
+ //! An output archive designed to save data in a compact binary representation
+ /*! This archive outputs data to a stream in an extremely compact binary
+ representation with as little extra metadata as possible.
+
+ This archive does nothing to ensure that the endianness of the saved
+ and loaded data is the same. If you need to have portability over
+ architectures with different endianness, use PortableBinaryOutputArchive.
+
+ When using a binary archive and a file stream, you must use the
+ std::ios::binary format flag to avoid having your data altered
+ inadvertently.
+
+ \ingroup Archives */
+ class BinaryOutputArchive : public OutputArchive
+ {
+ public:
+ //! Construct, outputting to the provided stream
+ /*! @param stream The stream to output to. Can be a stringstream, a file stream, or
+ even cout! */
+ BinaryOutputArchive(std::ostream & stream) :
+ OutputArchive(this),
+ itsStream(stream)
+ { }
+
+ ~BinaryOutputArchive() CEREAL_NOEXCEPT = default;
+
+ //! Writes size bytes of data to the output stream
+ void saveBinary( const void * data, std::streamsize size )
+ {
+ auto const writtenSize = itsStream.rdbuf()->sputn( reinterpret_cast( data ), size );
+
+ if(writtenSize != size)
+ throw Exception("Failed to write " + std::to_string(size) + " bytes to output stream! Wrote " + std::to_string(writtenSize));
+ }
+
+ private:
+ std::ostream & itsStream;
+ };
+
+ // ######################################################################
+ //! An input archive designed to load data saved using BinaryOutputArchive
+ /* This archive does nothing to ensure that the endianness of the saved
+ and loaded data is the same. If you need to have portability over
+ architectures with different endianness, use PortableBinaryOutputArchive.
+
+ When using a binary archive and a file stream, you must use the
+ std::ios::binary format flag to avoid having your data altered
+ inadvertently.
+
+ \ingroup Archives */
+ class BinaryInputArchive : public InputArchive
+ {
+ public:
+ //! Construct, loading from the provided stream
+ BinaryInputArchive(std::istream & stream) :
+ InputArchive(this),
+ itsStream(stream)
+ { }
+
+ ~BinaryInputArchive() CEREAL_NOEXCEPT = default;
+
+ //! Reads size bytes of data from the input stream
+ void loadBinary( void * const data, std::streamsize size )
+ {
+ auto const readSize = itsStream.rdbuf()->sgetn( reinterpret_cast( data ), size );
+
+ if(readSize != size)
+ throw Exception("Failed to read " + std::to_string(size) + " bytes from input stream! Read " + std::to_string(readSize));
+ }
+
+ private:
+ std::istream & itsStream;
+ };
+
+ // ######################################################################
+ // Common BinaryArchive serialization functions
+
+ //! Saving for POD types to binary
+ template inline
+ typename std::enable_if::value, void>::type
+ CEREAL_SAVE_FUNCTION_NAME(BinaryOutputArchive & ar, T const & t)
+ {
+ ar.saveBinary(std::addressof(t), sizeof(t));
+ }
+
+ //! Loading for POD types from binary
+ template inline
+ typename std::enable_if::value, void>::type
+ CEREAL_LOAD_FUNCTION_NAME(BinaryInputArchive & ar, T & t)
+ {
+ ar.loadBinary(std::addressof(t), sizeof(t));
+ }
+
+ //! Serializing NVP types to binary
+ template inline
+ CEREAL_ARCHIVE_RESTRICT(BinaryInputArchive, BinaryOutputArchive)
+ CEREAL_SERIALIZE_FUNCTION_NAME( Archive & ar, NameValuePair & t )
+ {
+ ar( t.value );
+ }
+
+ //! Serializing SizeTags to binary
+ template inline
+ CEREAL_ARCHIVE_RESTRICT(BinaryInputArchive, BinaryOutputArchive)
+ CEREAL_SERIALIZE_FUNCTION_NAME( Archive & ar, SizeTag & t )
+ {
+ ar( t.size );
+ }
+
+ //! Saving binary data
+ template inline
+ void CEREAL_SAVE_FUNCTION_NAME(BinaryOutputArchive & ar, BinaryData const & bd)
+ {
+ ar.saveBinary( bd.data, static_cast( bd.size ) );
+ }
+
+ //! Loading binary data
+ template inline
+ void CEREAL_LOAD_FUNCTION_NAME(BinaryInputArchive & ar, BinaryData & bd)
+ {
+ ar.loadBinary(bd.data, static_cast( bd.size ) );
+ }
+} // namespace cereal
+
+// register archives for polymorphic support
+CEREAL_REGISTER_ARCHIVE(cereal::BinaryOutputArchive)
+CEREAL_REGISTER_ARCHIVE(cereal::BinaryInputArchive)
+
+// tie input and output archives together
+CEREAL_SETUP_ARCHIVE_TRAITS(cereal::BinaryInputArchive, cereal::BinaryOutputArchive)
+
+#endif // CEREAL_ARCHIVES_BINARY_HPP_
diff --git a/src/include/cereal/archives/json.hpp b/src/include/cereal/archives/json.hpp
new file mode 100644
index 0000000..199efc9
--- /dev/null
+++ b/src/include/cereal/archives/json.hpp
@@ -0,0 +1,1024 @@
+/*! \file json.hpp
+ \brief JSON input and output archives */
+/*
+ Copyright (c) 2014, Randolph Voorhies, Shane Grant
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the copyright holder nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
+ DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+#ifndef CEREAL_ARCHIVES_JSON_HPP_
+#define CEREAL_ARCHIVES_JSON_HPP_
+
+#include "cereal/cereal.hpp"
+#include "cereal/details/util.hpp"
+
+namespace cereal
+{
+ //! An exception thrown when rapidjson fails an internal assertion
+ /*! @ingroup Utility */
+ struct RapidJSONException : Exception
+ { RapidJSONException( const char * what_ ) : Exception( what_ ) {} };
+}
+
+// Inform rapidjson that assert will throw
+#ifndef CEREAL_RAPIDJSON_ASSERT_THROWS
+#define CEREAL_RAPIDJSON_ASSERT_THROWS
+#endif // CEREAL_RAPIDJSON_ASSERT_THROWS
+
+// Override rapidjson assertions to throw exceptions by default
+#ifndef CEREAL_RAPIDJSON_ASSERT
+#define CEREAL_RAPIDJSON_ASSERT(x) if(!(x)){ \
+ throw ::cereal::RapidJSONException("rapidjson internal assertion failure: " #x); }
+#endif // RAPIDJSON_ASSERT
+
+// Enable support for parsing of nan, inf, -inf
+#ifndef CEREAL_RAPIDJSON_WRITE_DEFAULT_FLAGS
+#define CEREAL_RAPIDJSON_WRITE_DEFAULT_FLAGS kWriteNanAndInfFlag
+#endif
+
+// Enable support for parsing of nan, inf, -inf
+#ifndef CEREAL_RAPIDJSON_PARSE_DEFAULT_FLAGS
+#define CEREAL_RAPIDJSON_PARSE_DEFAULT_FLAGS kParseFullPrecisionFlag | kParseNanAndInfFlag
+#endif
+
+#include "cereal/external/rapidjson/prettywriter.h"
+#include "cereal/external/rapidjson/ostreamwrapper.h"
+#include "cereal/external/rapidjson/istreamwrapper.h"
+#include "cereal/external/rapidjson/document.h"
+#include "cereal/external/base64.hpp"
+
+#include
+#include
+#include
+#include
+#include
+
+namespace cereal
+{
+ // ######################################################################
+ //! An output archive designed to save data to JSON
+ /*! This archive uses RapidJSON to build serialize data to JSON.
+
+ JSON archives provides a human readable output but at decreased
+ performance (both in time and space) compared to binary archives.
+
+ JSON archives are only guaranteed to finish flushing their contents
+ upon destruction and should thus be used in an RAII fashion.
+
+ JSON benefits greatly from name-value pairs, which if present, will
+ name the nodes in the output. If these are not present, each level
+ of the output will be given an automatically generated delimited name.
+
+ The precision of the output archive controls the number of decimals output
+ for floating point numbers and should be sufficiently large (i.e. at least 20)
+ if there is a desire to have binary equality between the numbers output and
+ those read in. In general you should expect a loss of precision when going
+ from floating point to text and back.
+
+ JSON archives do not output the size information for any dynamically sized structure
+ and instead infer it from the number of children for a node. This means that data
+ can be hand edited for dynamic sized structures and will still be readable. This
+ is accomplished through the cereal::SizeTag object, which will cause the archive
+ to output the data as a JSON array (e.g. marked by [] instead of {}), which indicates
+ that the container is variable sized and may be edited.
+
+ \ingroup Archives */
+ class JSONOutputArchive : public OutputArchive, public traits::TextArchive
+ {
+ enum class NodeType { StartObject, InObject, StartArray, InArray };
+
+ using WriteStream = CEREAL_RAPIDJSON_NAMESPACE::OStreamWrapper;
+ using JSONWriter = CEREAL_RAPIDJSON_NAMESPACE::PrettyWriter;
+
+ public:
+ /*! @name Common Functionality
+ Common use cases for directly interacting with an JSONOutputArchive */
+ //! @{
+
+ //! A class containing various advanced options for the JSON archive
+ class Options
+ {
+ public:
+ //! Default options
+ static Options Default(){ return Options(); }
+
+ //! Default options with no indentation
+ static Options NoIndent(){ return Options( JSONWriter::kDefaultMaxDecimalPlaces, IndentChar::space, 0 ); }
+
+ //! The character to use for indenting
+ enum class IndentChar : char
+ {
+ space = ' ',
+ tab = '\t',
+ newline = '\n',
+ carriage_return = '\r'
+ };
+
+ //! Specify specific options for the JSONOutputArchive
+ /*! @param precision The precision used for floating point numbers
+ @param indentChar The type of character to indent with
+ @param indentLength The number of indentChar to use for indentation
+ (0 corresponds to no indentation) */
+ explicit Options( int precision = JSONWriter::kDefaultMaxDecimalPlaces,
+ IndentChar indentChar = IndentChar::space,
+ unsigned int indentLength = 4 ) :
+ itsPrecision( precision ),
+ itsIndentChar( static_cast(indentChar) ),
+ itsIndentLength( indentLength ) { }
+
+ private:
+ friend class JSONOutputArchive;
+ int itsPrecision;
+ char itsIndentChar;
+ unsigned int itsIndentLength;
+ };
+
+ //! Construct, outputting to the provided stream
+ /*! @param stream The stream to output to.
+ @param options The JSON specific options to use. See the Options struct
+ for the values of default parameters */
+ JSONOutputArchive(std::ostream & stream, Options const & options = Options::Default() ) :
+ OutputArchive(this),
+ itsWriteStream(stream),
+ itsWriter(itsWriteStream),
+ itsNextName(nullptr)
+ {
+ itsWriter.SetMaxDecimalPlaces( options.itsPrecision );
+ itsWriter.SetIndent( options.itsIndentChar, options.itsIndentLength );
+ itsNameCounter.push(0);
+ itsNodeStack.push(NodeType::StartObject);
+ }
+
+ //! Destructor, flushes the JSON
+ ~JSONOutputArchive() CEREAL_NOEXCEPT
+ {
+ if (itsNodeStack.top() == NodeType::InObject)
+ itsWriter.EndObject();
+ else if (itsNodeStack.top() == NodeType::InArray)
+ itsWriter.EndArray();
+ }
+
+ //! Saves some binary data, encoded as a base64 string, with an optional name
+ /*! This will create a new node, optionally named, and insert a value that consists of
+ the data encoded as a base64 string */
+ void saveBinaryValue( const void * data, size_t size, const char * name = nullptr )
+ {
+ setNextName( name );
+ writeName();
+
+ auto base64string = base64::encode( reinterpret_cast( data ), size );
+ saveValue( base64string );
+ };
+
+ //! @}
+ /*! @name Internal Functionality
+ Functionality designed for use by those requiring control over the inner mechanisms of
+ the JSONOutputArchive */
+ //! @{
+
+ //! Starts a new node in the JSON output
+ /*! The node can optionally be given a name by calling setNextName prior
+ to creating the node
+
+ Nodes only need to be started for types that are themselves objects or arrays */
+ void startNode()
+ {
+ writeName();
+ itsNodeStack.push(NodeType::StartObject);
+ itsNameCounter.push(0);
+ }
+
+ //! Designates the most recently added node as finished
+ void finishNode()
+ {
+ // if we ended up serializing an empty object or array, writeName
+ // will never have been called - so start and then immediately end
+ // the object/array.
+ //
+ // We'll also end any object/arrays we happen to be in
+ switch(itsNodeStack.top())
+ {
+ case NodeType::StartArray:
+ itsWriter.StartArray();
+ // fall through
+ case NodeType::InArray:
+ itsWriter.EndArray();
+ break;
+ case NodeType::StartObject:
+ itsWriter.StartObject();
+ // fall through
+ case NodeType::InObject:
+ itsWriter.EndObject();
+ break;
+ }
+
+ itsNodeStack.pop();
+ itsNameCounter.pop();
+ }
+
+ //! Sets the name for the next node created with startNode
+ void setNextName( const char * name )
+ {
+ itsNextName = name;
+ }
+
+ //! Saves a bool to the current node
+ void saveValue(bool b) { itsWriter.Bool(b); }
+ //! Saves an int to the current node
+ void saveValue(int i) { itsWriter.Int(i); }
+ //! Saves a uint to the current node
+ void saveValue(unsigned u) { itsWriter.Uint(u); }
+ //! Saves an int64 to the current node
+ void saveValue(int64_t i64) { itsWriter.Int64(i64); }
+ //! Saves a uint64 to the current node
+ void saveValue(uint64_t u64) { itsWriter.Uint64(u64); }
+ //! Saves a double to the current node
+ void saveValue(double d) { itsWriter.Double(d); }
+ //! Saves a string to the current node
+ void saveValue(std::string const & s) { itsWriter.String(s.c_str(), static_cast( s.size() )); }
+ //! Saves a const char * to the current node
+ void saveValue(char const * s) { itsWriter.String(s); }
+ //! Saves a nullptr to the current node
+ void saveValue(std::nullptr_t) { itsWriter.Null(); }
+
+ private:
+ // Some compilers/OS have difficulty disambiguating the above for various flavors of longs, so we provide
+ // special overloads to handle these cases.
+
+ //! 32 bit signed long saving to current node
+ template ::value> = traits::sfinae> inline
+ void saveLong(T l){ saveValue( static_cast( l ) ); }
+
+ //! non 32 bit signed long saving to current node
+ template ::value> = traits::sfinae> inline
+ void saveLong(T l){ saveValue( static_cast( l ) ); }
+
+ //! 32 bit unsigned long saving to current node
+ template ::value> = traits::sfinae> inline
+ void saveLong(T lu){ saveValue( static_cast( lu ) ); }
+
+ //! non 32 bit unsigned long saving to current node
+ template ::value> = traits::sfinae> inline
+ void saveLong(T lu){ saveValue( static_cast( lu ) ); }
+
+ public:
+#if defined(_MSC_VER) && _MSC_VER < 1916
+ //! MSVC only long overload to current node
+ void saveValue( unsigned long lu ){ saveLong( lu ); };
+#else // _MSC_VER
+ //! Serialize a long if it would not be caught otherwise
+ template ::value,
+ !std::is_same::value,
+ !std::is_same::value> = traits::sfinae> inline
+ void saveValue( T t ){ saveLong( t ); }
+
+ //! Serialize an unsigned long if it would not be caught otherwise
+ template ::value,
+ !std::is_same::value,
+ !std::is_same::value> = traits::sfinae> inline
+ void saveValue( T t ){ saveLong( t ); }
+#endif // _MSC_VER
+
+ //! Save exotic arithmetic as strings to current node
+ /*! Handles long long (if distinct from other types), unsigned long (if distinct), and long double */
+ template ::value,
+ !std::is_same::value,
+ !std::is_same::value,
+ !std::is_same::value,
+ !std::is_same::value,
+ (sizeof(T) >= sizeof(long double) || sizeof(T) >= sizeof(long long))> = traits::sfinae> inline
+ void saveValue(T const & t)
+ {
+ std::stringstream ss; ss.precision( std::numeric_limits::max_digits10 );
+ ss << t;
+ saveValue( ss.str() );
+ }
+
+ //! Write the name of the upcoming node and prepare object/array state
+ /*! Since writeName is called for every value that is output, regardless of
+ whether it has a name or not, it is the place where we will do a deferred
+ check of our node state and decide whether we are in an array or an object.
+
+ The general workflow of saving to the JSON archive is:
+
+ 1. (optional) Set the name for the next node to be created, usually done by an NVP
+ 2. Start the node
+ 3. (if there is data to save) Write the name of the node (this function)
+ 4. (if there is data to save) Save the data (with saveValue)
+ 5. Finish the node
+ */
+ void writeName()
+ {
+ NodeType const & nodeType = itsNodeStack.top();
+
+ // Start up either an object or an array, depending on state
+ if(nodeType == NodeType::StartArray)
+ {
+ itsWriter.StartArray();
+ itsNodeStack.top() = NodeType::InArray;
+ }
+ else if(nodeType == NodeType::StartObject)
+ {
+ itsNodeStack.top() = NodeType::InObject;
+ itsWriter.StartObject();
+ }
+
+ // Array types do not output names
+ if(nodeType == NodeType::InArray) return;
+
+ if(itsNextName == nullptr)
+ {
+ std::string name = "value" + std::to_string( itsNameCounter.top()++ ) + "\0";
+ saveValue(name);
+ }
+ else
+ {
+ saveValue(itsNextName);
+ itsNextName = nullptr;
+ }
+ }
+
+ //! Designates that the current node should be output as an array, not an object
+ void makeArray()
+ {
+ itsNodeStack.top() = NodeType::StartArray;
+ }
+
+ //! @}
+
+ private:
+ WriteStream itsWriteStream; //!< Rapidjson write stream
+ JSONWriter itsWriter; //!< Rapidjson writer
+ char const * itsNextName; //!< The next name
+ std::stack itsNameCounter; //!< Counter for creating unique names for unnamed nodes
+ std::stack itsNodeStack;
+ }; // JSONOutputArchive
+
+ // ######################################################################
+ //! An input archive designed to load data from JSON
+ /*! This archive uses RapidJSON to read in a JSON archive.
+
+ As with the output JSON archive, the preferred way to use this archive is in
+ an RAII fashion, ensuring its destruction after all data has been read.
+
+ Input JSON should have been produced by the JSONOutputArchive. Data can
+ only be added to dynamically sized containers (marked by JSON arrays) -
+ the input archive will determine their size by looking at the number of child nodes.
+ Only JSON originating from a JSONOutputArchive is officially supported, but data
+ from other sources may work if properly formatted.
+
+ The JSONInputArchive does not require that nodes are loaded in the same
+ order they were saved by JSONOutputArchive. Using name value pairs (NVPs),
+ it is possible to load in an out of order fashion or otherwise skip/select
+ specific nodes to load.
+
+ The default behavior of the input archive is to read sequentially starting
+ with the first node and exploring its children. When a given NVP does
+ not match the read in name for a node, the archive will search for that
+ node at the current level and load it if it exists. After loading an out of
+ order node, the archive will then proceed back to loading sequentially from
+ its new position.
+
+ Consider this simple example where loading of some data is skipped:
+
+ @code{cpp}
+ // imagine the input file has someData(1-9) saved in order at the top level node
+ ar( someData1, someData2, someData3 ); // XML loads in the order it sees in the file
+ ar( cereal::make_nvp( "hello", someData6 ) ); // NVP given does not
+ // match expected NVP name, so we search
+ // for the given NVP and load that value
+ ar( someData7, someData8, someData9 ); // with no NVP given, loading resumes at its
+ // current location, proceeding sequentially
+ @endcode
+
+ \ingroup Archives */
+ class JSONInputArchive : public InputArchive, public traits::TextArchive
+ {
+ private:
+ using ReadStream = CEREAL_RAPIDJSON_NAMESPACE::IStreamWrapper;
+ typedef CEREAL_RAPIDJSON_NAMESPACE::GenericValue> JSONValue;
+ typedef JSONValue::ConstMemberIterator MemberIterator;
+ typedef JSONValue::ConstValueIterator ValueIterator;
+ typedef CEREAL_RAPIDJSON_NAMESPACE::Document::GenericValue GenericValue;
+
+ public:
+ /*! @name Common Functionality
+ Common use cases for directly interacting with an JSONInputArchive */
+ //! @{
+
+ //! Construct, reading from the provided stream
+ /*! @param stream The stream to read from */
+ JSONInputArchive(std::istream & stream) :
+ InputArchive(this),
+ itsNextName( nullptr ),
+ itsReadStream(stream)
+ {
+ itsDocument.ParseStream<>(itsReadStream);
+ if (itsDocument.IsArray())
+ itsIteratorStack.emplace_back(itsDocument.Begin(), itsDocument.End());
+ else
+ itsIteratorStack.emplace_back(itsDocument.MemberBegin(), itsDocument.MemberEnd());
+ }
+
+ ~JSONInputArchive() CEREAL_NOEXCEPT = default;
+
+ //! Loads some binary data, encoded as a base64 string
+ /*! This will automatically start and finish a node to load the data, and can be called directly by
+ users.
+
+ Note that this follows the same ordering rules specified in the class description in regards
+ to loading in/out of order */
+ void loadBinaryValue( void * data, size_t size, const char * name = nullptr )
+ {
+ itsNextName = name;
+
+ std::string encoded;
+ loadValue( encoded );
+ auto decoded = base64::decode( encoded );
+
+ if( size != decoded.size() )
+ throw Exception("Decoded binary data size does not match specified size");
+
+ std::memcpy( data, decoded.data(), decoded.size() );
+ itsNextName = nullptr;
+ };
+
+ private:
+ //! @}
+ /*! @name Internal Functionality
+ Functionality designed for use by those requiring control over the inner mechanisms of
+ the JSONInputArchive */
+ //! @{
+
+ //! An internal iterator that handles both array and object types
+ /*! This class is a variant and holds both types of iterators that
+ rapidJSON supports - one for arrays and one for objects. */
+ class Iterator
+ {
+ public:
+ Iterator() : itsIndex( 0 ), itsType(Null_) {}
+
+ Iterator(MemberIterator begin, MemberIterator end) :
+ itsMemberItBegin(begin), itsMemberItEnd(end), itsIndex(0), itsSize(std::distance(begin, end)), itsType(Member)
+ {
+ if( itsSize == 0 )
+ itsType = Null_;
+ }
+
+ Iterator(ValueIterator begin, ValueIterator end) :
+ itsValueItBegin(begin), itsIndex(0), itsSize(std::distance(begin, end)), itsType(Value)
+ {
+ if( itsSize == 0 )
+ itsType = Null_;
+ }
+
+ //! Advance to the next node
+ Iterator & operator++()
+ {
+ ++itsIndex;
+ return *this;
+ }
+
+ //! Get the value of the current node
+ GenericValue const & value()
+ {
+ if( itsIndex >= itsSize )
+ throw cereal::Exception("No more objects in input");
+
+ switch(itsType)
+ {
+ case Value : return itsValueItBegin[itsIndex];
+ case Member: return itsMemberItBegin[itsIndex].value;
+ default: throw cereal::Exception("JSONInputArchive internal error: null or empty iterator to object or array!");
+ }
+ }
+
+ //! Get the name of the current node, or nullptr if it has no name
+ const char * name() const
+ {
+ if( itsType == Member && (itsMemberItBegin + itsIndex) != itsMemberItEnd )
+ return itsMemberItBegin[itsIndex].name.GetString();
+ else
+ return nullptr;
+ }
+
+ //! Adjust our position such that we are at the node with the given name
+ /*! @throws Exception if no such named node exists */
+ inline void search( const char * searchName )
+ {
+ const auto len = std::strlen( searchName );
+ size_t index = 0;
+ for( auto it = itsMemberItBegin; it != itsMemberItEnd; ++it, ++index )
+ {
+ const auto currentName = it->name.GetString();
+ if( ( std::strncmp( searchName, currentName, len ) == 0 ) &&
+ ( std::strlen( currentName ) == len ) )
+ {
+ itsIndex = index;
+ return;
+ }
+ }
+
+ throw Exception("JSON Parsing failed - provided NVP (" + std::string(searchName) + ") not found");
+ }
+
+ private:
+ MemberIterator itsMemberItBegin, itsMemberItEnd; //!< The member iterator (object)
+ ValueIterator itsValueItBegin; //!< The value iterator (array)
+ size_t itsIndex, itsSize; //!< The current index of this iterator
+ enum Type {Value, Member, Null_} itsType; //!< Whether this holds values (array) or members (objects) or nothing
+ };
+
+ //! Searches for the expectedName node if it doesn't match the actualName
+ /*! This needs to be called before every load or node start occurs. This function will
+ check to see if an NVP has been provided (with setNextName) and if so, see if that name matches the actual
+ next name given. If the names do not match, it will search in the current level of the JSON for that name.
+ If the name is not found, an exception will be thrown.
+
+ Resets the NVP name after called.
+
+ @throws Exception if an expectedName is given and not found */
+ inline void search()
+ {
+ // store pointer to itsNextName locally and reset to nullptr in case search() throws
+ auto localNextName = itsNextName;
+ itsNextName = nullptr;
+
+ // The name an NVP provided with setNextName()
+ if( localNextName )
+ {
+ // The actual name of the current node
+ auto const actualName = itsIteratorStack.back().name();
+
+ // Do a search if we don't see a name coming up, or if the names don't match
+ if( !actualName || std::strcmp( localNextName, actualName ) != 0 )
+ itsIteratorStack.back().search( localNextName );
+ }
+ }
+
+ public:
+ //! Starts a new node, going into its proper iterator
+ /*! This places an iterator for the next node to be parsed onto the iterator stack. If the next
+ node is an array, this will be a value iterator, otherwise it will be a member iterator.
+
+ By default our strategy is to start with the document root node and then recursively iterate through
+ all children in the order they show up in the document.
+ We don't need to know NVPs to do this; we'll just blindly load in the order things appear in.
+
+ If we were given an NVP, we will search for it if it does not match our the name of the next node
+ that would normally be loaded. This functionality is provided by search(). */
+ void startNode()
+ {
+ search();
+
+ if(itsIteratorStack.back().value().IsArray())
+ itsIteratorStack.emplace_back(itsIteratorStack.back().value().Begin(), itsIteratorStack.back().value().End());
+ else
+ itsIteratorStack.emplace_back(itsIteratorStack.back().value().MemberBegin(), itsIteratorStack.back().value().MemberEnd());
+ }
+
+ //! Finishes the most recently started node
+ void finishNode()
+ {
+ itsIteratorStack.pop_back();
+ ++itsIteratorStack.back();
+ }
+
+ //! Retrieves the current node name
+ /*! @return nullptr if no name exists */
+ const char * getNodeName() const
+ {
+ return itsIteratorStack.back().name();
+ }
+
+ //! Sets the name for the next node created with startNode
+ void setNextName( const char * name )
+ {
+ itsNextName = name;
+ }
+
+ //! Loads a value from the current node - small signed overload
+ template ::value,
+ sizeof(T) < sizeof(int64_t)> = traits::sfinae> inline
+ void loadValue(T & val)
+ {
+ search();
+
+ val = static_cast( itsIteratorStack.back().value().GetInt() );
+ ++itsIteratorStack.back();
+ }
+
+ //! Loads a value from the current node - small unsigned overload
+ template ::value,
+ sizeof(T) < sizeof(uint64_t),
+ !std::is_same::value> = traits::sfinae> inline
+ void loadValue(T & val)
+ {
+ search();
+
+ val = static_cast( itsIteratorStack.back().value().GetUint() );
+ ++itsIteratorStack.back();
+ }
+
+ //! Loads a value from the current node - bool overload
+ void loadValue(bool & val) { search(); val = itsIteratorStack.back().value().GetBool(); ++itsIteratorStack.back(); }
+ //! Loads a value from the current node - int64 overload
+ void loadValue(int64_t & val) { search(); val = itsIteratorStack.back().value().GetInt64(); ++itsIteratorStack.back(); }
+ //! Loads a value from the current node - uint64 overload
+ void loadValue(uint64_t & val) { search(); val = itsIteratorStack.back().value().GetUint64(); ++itsIteratorStack.back(); }
+ //! Loads a value from the current node - float overload
+ void loadValue(float & val) { search(); val = static_cast(itsIteratorStack.back().value().GetDouble()); ++itsIteratorStack.back(); }
+ //! Loads a value from the current node - double overload
+ void loadValue(double & val) { search(); val = itsIteratorStack.back().value().GetDouble(); ++itsIteratorStack.back(); }
+ //! Loads a value from the current node - string overload
+ void loadValue(std::string & val) { search(); val = itsIteratorStack.back().value().GetString(); ++itsIteratorStack.back(); }
+ //! Loads a nullptr from the current node
+ void loadValue(std::nullptr_t&) { search(); CEREAL_RAPIDJSON_ASSERT(itsIteratorStack.back().value().IsNull()); ++itsIteratorStack.back(); }
+
+ // Special cases to handle various flavors of long, which tend to conflict with
+ // the int32_t or int64_t on various compiler/OS combinations. MSVC doesn't need any of this.
+ #ifndef _MSC_VER
+ private:
+ //! 32 bit signed long loading from current node
+ template inline
+ typename std::enable_if::value, void>::type
+ loadLong(T & l){ loadValue( reinterpret_cast( l ) ); }
+
+ //! non 32 bit signed long loading from current node
+ template inline
+ typename std::enable_if::value, void>::type
+ loadLong(T & l){ loadValue( reinterpret_cast( l ) ); }
+
+ //! 32 bit unsigned long loading from current node
+ template inline
+ typename std::enable_if::value, void>::type
+ loadLong(T & lu){ loadValue( reinterpret_cast( lu ) ); }
+
+ //! non 32 bit unsigned long loading from current node
+ template inline
+ typename std::enable_if::value, void>::type
+ loadLong(T & lu){ loadValue( reinterpret_cast( lu ) ); }
+
+ public:
+ //! Serialize a long if it would not be caught otherwise
+ template inline
+ typename std::enable_if::value &&
+ sizeof(T) >= sizeof(std::int64_t) &&
+ !std::is_same::value, void>::type
+ loadValue( T & t ){ loadLong(t); }
+
+ //! Serialize an unsigned long if it would not be caught otherwise
+ template inline
+ typename std::enable_if::value &&
+ sizeof(T) >= sizeof(std::uint64_t) &&
+ !std::is_same::value, void>::type
+ loadValue( T & t ){ loadLong(t); }
+ #endif // _MSC_VER
+
+ private:
+ //! Convert a string to a long long
+ void stringToNumber( std::string const & str, long long & val ) { val = std::stoll( str ); }
+ //! Convert a string to an unsigned long long
+ void stringToNumber( std::string const & str, unsigned long long & val ) { val = std::stoull( str ); }
+ //! Convert a string to a long double
+ void stringToNumber( std::string const & str, long double & val ) { val = std::stold( str ); }
+
+ public:
+ //! Loads a value from the current node - long double and long long overloads
+ template ::value,
+ !std::is_same::value,
+ !std::is_same::value,
+ !std::is_same::value,
+ !std::is_same::value,
+ (sizeof(T) >= sizeof(long double) || sizeof(T) >= sizeof(long long))> = traits::sfinae>
+ inline void loadValue(T & val)
+ {
+ std::string encoded;
+ loadValue( encoded );
+ stringToNumber( encoded, val );
+ }
+
+ //! Loads the size for a SizeTag
+ void loadSize(size_type & size)
+ {
+ if (itsIteratorStack.size() == 1)
+ size = itsDocument.Size();
+ else
+ size = (itsIteratorStack.rbegin() + 1)->value().Size();
+ }
+
+ //! @}
+
+ private:
+ const char * itsNextName; //!< Next name set by NVP
+ ReadStream itsReadStream; //!< Rapidjson write stream
+ std::vector itsIteratorStack; //!< 'Stack' of rapidJSON iterators
+ CEREAL_RAPIDJSON_NAMESPACE::Document itsDocument; //!< Rapidjson document
+ };
+
+ // ######################################################################
+ // JSONArchive prologue and epilogue functions
+ // ######################################################################
+
+ // ######################################################################
+ //! Prologue for NVPs for JSON archives
+ /*! NVPs do not start or finish nodes - they just set up the names */
+ template inline
+ void prologue( JSONOutputArchive &, NameValuePair const & )
+ { }
+
+ //! Prologue for NVPs for JSON archives
+ template inline
+ void prologue( JSONInputArchive &, NameValuePair const & )
+ { }
+
+ // ######################################################################
+ //! Epilogue for NVPs for JSON archives
+ /*! NVPs do not start or finish nodes - they just set up the names */
+ template inline
+ void epilogue( JSONOutputArchive &, NameValuePair const & )
+ { }
+
+ //! Epilogue for NVPs for JSON archives
+ /*! NVPs do not start or finish nodes - they just set up the names */
+ template inline
+ void epilogue( JSONInputArchive &, NameValuePair const & )
+ { }
+
+ // ######################################################################
+ //! Prologue for deferred data for JSON archives
+ /*! Do nothing for the defer wrapper */
+ template inline
+ void prologue( JSONOutputArchive &, DeferredData const & )
+ { }
+
+ //! Prologue for deferred data for JSON archives
+ template inline
+ void prologue( JSONInputArchive &, DeferredData const & )
+ { }
+
+ // ######################################################################
+ //! Epilogue for deferred for JSON archives
+ /*! NVPs do not start or finish nodes - they just set up the names */
+ template inline
+ void epilogue( JSONOutputArchive &, DeferredData const & )
+ { }
+
+ //! Epilogue for deferred for JSON archives
+ /*! Do nothing for the defer wrapper */
+ template inline
+ void epilogue( JSONInputArchive &, DeferredData const & )
+ { }
+
+ // ######################################################################
+ //! Prologue for SizeTags for JSON archives
+ /*! SizeTags are strictly ignored for JSON, they just indicate
+ that the current node should be made into an array */
+ template inline
+ void prologue( JSONOutputArchive & ar, SizeTag const & )
+ {
+ ar.makeArray();
+ }
+
+ //! Prologue for SizeTags for JSON archives
+ template inline
+ void prologue( JSONInputArchive &, SizeTag const & )
+ { }
+
+ // ######################################################################
+ //! Epilogue for SizeTags for JSON archives
+ /*! SizeTags are strictly ignored for JSON */
+ template inline
+ void epilogue( JSONOutputArchive &, SizeTag const & )
+ { }
+
+ //! Epilogue for SizeTags for JSON archives
+ template inline
+ void epilogue( JSONInputArchive &, SizeTag const & )
+ { }
+
+ // ######################################################################
+ //! Prologue for all other types for JSON archives (except minimal types)
+ /*! Starts a new node, named either automatically or by some NVP,
+ that may be given data by the type about to be archived
+
+ Minimal types do not start or finish nodes */
+ template ::value,
+ !traits::has_minimal_base_class_serialization::value,
+ !traits::has_minimal_output_serialization::value> = traits::sfinae>
+ inline void prologue( JSONOutputArchive & ar, T const & )
+ {
+ ar.startNode();
+ }
+
+ //! Prologue for all other types for JSON archives
+ template ::value,
+ !traits::has_minimal_base_class_serialization::value,
+ !traits::has_minimal_input_serialization::value> = traits::sfinae>
+ inline void prologue( JSONInputArchive & ar, T const & )
+ {
+ ar.startNode();
+ }
+
+ // ######################################################################
+ //! Epilogue for all other types other for JSON archives (except minimal types)
+ /*! Finishes the node created in the prologue
+
+ Minimal types do not start or finish nodes */
+ template ::value,
+ !traits::has_minimal_base_class_serialization::value,
+ !traits::has_minimal_output_serialization::value> = traits::sfinae>
+ inline void epilogue( JSONOutputArchive & ar, T const & )
+ {
+ ar.finishNode();
+ }
+
+ //! Epilogue for all other types other for JSON archives
+ template ::value,
+ !traits::has_minimal_base_class_serialization::value,
+ !traits::has_minimal_input_serialization::value> = traits::sfinae>
+ inline void epilogue( JSONInputArchive & ar, T const & )
+ {
+ ar.finishNode();
+ }
+
+ // ######################################################################
+ //! Prologue for arithmetic types for JSON archives
+ inline
+ void prologue( JSONOutputArchive & ar, std::nullptr_t const & )
+ {
+ ar.writeName();
+ }
+
+ //! Prologue for arithmetic types for JSON archives
+ inline
+ void prologue( JSONInputArchive &, std::nullptr_t const & )
+ { }
+
+ // ######################################################################
+ //! Epilogue for arithmetic types for JSON archives
+ inline
+ void epilogue( JSONOutputArchive &, std::nullptr_t const & )
+ { }
+
+ //! Epilogue for arithmetic types for JSON archives
+ inline
+ void epilogue( JSONInputArchive &, std::nullptr_t const & )
+ { }
+
+ // ######################################################################
+ //! Prologue for arithmetic types for JSON archives
+ template ::value> = traits::sfinae> inline
+ void prologue( JSONOutputArchive & ar, T const & )
+ {
+ ar.writeName();
+ }
+
+ //! Prologue for arithmetic types for JSON archives
+ template ::value> = traits::sfinae> inline
+ void prologue( JSONInputArchive &, T const & )
+ { }
+
+ // ######################################################################
+ //! Epilogue for arithmetic types for JSON archives
+ template ::value> = traits::sfinae> inline
+ void epilogue( JSONOutputArchive &, T const & )
+ { }
+
+ //! Epilogue for arithmetic types for JSON archives
+ template ::value> = traits::sfinae> inline
+ void epilogue( JSONInputArchive &, T const & )
+ { }
+
+ // ######################################################################
+ //! Prologue for strings for JSON archives
+ template