Skip to content

Commit 09a88d4

Browse files
committed
Custom class xarray with shorter "of" initialization
1 parent 4e3ec1b commit 09a88d4

7 files changed

Lines changed: 462 additions & 44 deletions

File tree

include/xarray.h

Lines changed: 149 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,169 @@
11
//----------------------------------------------------------------------------------
22
// Name: xarray.h
3-
// Purpose: Definition of vector of xnodes.
3+
// Purpose: Custom array class for xnode objects.
44
// Author: Piotr Likus
55
// Created: 01/09/2015
6-
// Last change:
6+
// Last change: 15/05/2025
77
// License: BSD
88
//----------------------------------------------------------------------------------
99

1010
#ifndef __XNODE_ARRAY_H__
1111
#define __XNODE_ARRAY_H__
1212

1313
#include <vector>
14+
#include <initializer_list>
15+
#include <cassert>
1416
#include "xnode.h"
1517

16-
typedef std::vector<xnode> xarray;
18+
// Forward declaration
19+
class xarray;
1720

21+
// Type code registration for xarray
1822
template<>
1923
struct xnode_type_code<xarray> {
2024
enum { value = 16 }; // Ensure this doesn't conflict with other type codes
2125
};
2226

23-
#endif
27+
/**
28+
* Custom array class for xnode objects.
29+
* Provides a limited array interface with static initializer "of" that accepts variable number of xnode objects.
30+
*/
31+
class xarray {
32+
public:
33+
// Type definitions
34+
using value_type = xnode;
35+
using container_type = std::vector<value_type>;
36+
using reference = container_type::reference;
37+
using const_reference = container_type::const_reference;
38+
using iterator = container_type::iterator;
39+
using const_iterator = container_type::const_iterator;
40+
using size_type = container_type::size_type;
41+
using difference_type = container_type::difference_type;
42+
43+
// Constructors
44+
xarray() = default;
45+
46+
// Copy constructor
47+
xarray(const xarray& other) = default;
48+
49+
// Move constructor
50+
xarray(xarray&& other) noexcept = default;
51+
52+
// Construct from initializer list of xnodes
53+
xarray(std::initializer_list<value_type> init) : data_(init) {}
54+
55+
// Assignment operators
56+
xarray& operator=(const xarray& other) = default;
57+
xarray& operator=(xarray&& other) noexcept = default;
58+
59+
// Basic functions
60+
bool empty() const { return data_.empty(); }
61+
size_type size() const { return data_.size(); }
62+
63+
// Element access
64+
reference at(size_type pos) { return data_.at(pos); }
65+
const_reference at(size_type pos) const { return data_.at(pos); }
66+
67+
reference operator[](size_type pos) { return data_[pos]; }
68+
const_reference operator[](size_type pos) const { return data_[pos]; }
69+
70+
// Iterators
71+
iterator begin() { return data_.begin(); }
72+
const_iterator begin() const { return data_.begin(); }
73+
const_iterator cbegin() const { return data_.cbegin(); }
74+
75+
iterator end() { return data_.end(); }
76+
const_iterator end() const { return data_.end(); }
77+
const_iterator cend() const { return data_.cend(); }
78+
79+
// Capacity manipulation
80+
void reserve(size_type new_cap) { data_.reserve(new_cap); }
81+
size_type capacity() const { return data_.capacity(); }
82+
83+
// Modifiers
84+
void clear() { data_.clear(); }
85+
void push_back(const value_type& value) { data_.push_back(value); }
86+
void push_back(value_type&& value) { data_.push_back(std::move(value)); }
87+
iterator insert(const_iterator pos, const value_type& value) { return data_.insert(pos, value); }
88+
iterator insert(const_iterator pos, value_type&& value) { return data_.insert(pos, std::move(value)); }
89+
iterator erase(const_iterator pos) { return data_.erase(pos); }
90+
void resize(size_type count, const value_type& value = value_type()) { data_.resize(count, value); }
91+
// C++17 and above: Static factory function that accepts variable number of arguments
92+
// and converts them directly to xnode objects
93+
94+
#if __cplusplus >= 201703L
95+
// C++17 version that auto-converts values to xnode objects
96+
template <typename... Args>
97+
static xarray of(Args&&... args) {
98+
xarray result;
99+
result.reserve(sizeof...(args));
100+
(result.push_back(xnode::value_of(std::forward<Args>(args))), ...); // Fold expression (C++17)
101+
return result;
102+
}
103+
104+
// C++17 version for direct xnode objects (to avoid unnecessary conversion)
105+
template <typename... Args>
106+
static xarray of_nodes(const xnode& first, Args&&... args) {
107+
xarray result;
108+
result.reserve(sizeof...(args) + 1);
109+
result.push_back(first);
110+
(result.push_back(std::forward<Args>(args)), ...); // Fold expression (C++17)
111+
return result;
112+
}
113+
114+
// Specialization for empty array
115+
static xarray of_nodes() {
116+
return xarray();
117+
}
118+
#else
119+
// Pre-C++17: Recursive variadic template implementations
120+
121+
// Version that requires explicit xnode arguments
122+
static xarray of() {
123+
return xarray();
124+
}
125+
126+
template <typename... Args>
127+
static xarray of(const xnode& first, const Args&... rest) {
128+
xarray result;
129+
result.reserve(sizeof...(rest) + 1);
130+
result.push_back(first);
131+
appendToArray(result, rest...);
132+
return result;
133+
}
134+
135+
// Alias for consistency with the C++17 version
136+
static xarray of_nodes() {
137+
return xarray();
138+
}
139+
140+
template <typename... Args>
141+
static xarray of_nodes(const xnode& first, const Args&... rest) {
142+
return of(first, rest...);
143+
}
144+
145+
// Helper function for pre-C++17 implementation
146+
private:
147+
static void appendToArray(xarray& /* array */) {
148+
// Base case: no more elements to add
149+
}
150+
151+
template <typename... Args>
152+
static void appendToArray(xarray& array, const xnode& first, const Args&... rest) {
153+
array.push_back(first);
154+
appendToArray(array, rest...);
155+
}
156+
public:
157+
#endif
158+
159+
// Compatibility with algorithms that expect STL containers
160+
bool operator==(const xarray& other) const { return data_ == other.data_; }
161+
bool operator!=(const xarray& other) const { return data_ != other.data_; }
162+
bool operator<(const xarray& other) const { return data_ < other.data_; }
163+
164+
private:
165+
container_type data_;
166+
};
167+
168+
#endif // __XNODE_ARRAY_H__
24169

include/xnode_utils.h

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -82,14 +82,11 @@ namespace XN_CHECK_EQUALS
8282

8383
namespace XN_CHECK_LESS
8484
{
85-
typedef char no[7];
86-
template<typename T> no& operator < (const T&, const T&);
85+
template <typename T, typename = void>
86+
struct opLessExists : std::false_type {};
8787

8888
template <typename T>
89-
struct opLessExists
90-
{
91-
enum { value = (sizeof(*(T*)(0) < *(T*)(0)) != sizeof(no)) };
92-
};
89+
struct opLessExists<T, decltype(void(std::declval<T>() < std::declval<T>()))> : std::true_type {};
9390
}
9491

9592
template<typename T>

test/CMakeLists.txt

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,20 @@ target_link_libraries(xarray_test PRIVATE xnode)
3232
# Add xarray test to CTest
3333
add_test(NAME xarray_test COMMAND xarray_test)
3434

35+
# Add xarray of test
36+
add_executable(xarray_of_test xarray_of_test.cpp)
37+
target_link_libraries(xarray_of_test PRIVATE xnode)
38+
39+
# Add xarray of test to CTest
40+
add_test(NAME xarray_of_test COMMAND xarray_of_test)
41+
42+
# Add xarray of versions test
43+
add_executable(xarray_of_versions_test xarray_of_versions_test.cpp)
44+
target_link_libraries(xarray_of_versions_test PRIVATE xnode)
45+
46+
# Add xarray of versions test to CTest
47+
add_test(NAME xarray_of_versions_test COMMAND xarray_of_versions_test)
48+
3549
# Add xobject tests
3650
add_executable(xobject_test xobject_test.cpp)
3751
target_link_libraries(xobject_test PRIVATE xnode)
@@ -47,7 +61,7 @@ target_link_libraries(property_list_test PRIVATE xnode)
4761
add_test(NAME property_list_test COMMAND property_list_test)
4862

4963
# Install the test executable if needed (optional)
50-
install(TARGETS xnode_test xnode_convert_test xnode_type_test xnode_overflow_test xarray_test xobject_test property_list_test
64+
install(TARGETS xnode_test xnode_convert_test xnode_type_test xnode_overflow_test xarray_test xarray_of_test xarray_of_versions_test xobject_test property_list_test
5165
RUNTIME DESTINATION bin
5266
OPTIONAL
5367
)

test/xarray_of_test.cpp

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
//----------------------------------------------------------------------------------
2+
// Name: xarray_of_test.cpp
3+
// Purpose: Unit tests for xarray of() static initializer
4+
// Author: Piotr Likus
5+
// Created: May 15, 2025
6+
// License: BSD
7+
//----------------------------------------------------------------------------------
8+
9+
#include "xnode.h"
10+
#include "xarray.h"
11+
#include <iostream>
12+
#include <string>
13+
#include "cunit.h"
14+
#include "xobject.h"
15+
16+
using namespace std;
17+
18+
void TestArrayOf() {
19+
// Test the static "of" initializer with different numbers of arguments
20+
21+
// Empty array
22+
xarray empty = xarray::of_nodes();
23+
Assert(empty.empty(), "Empty array should be empty");
24+
Assert(empty.size() == 0, "Empty array size should be 0");
25+
26+
// Single element (using appropriate version based on C++ standard)
27+
#if __cplusplus >= 201703L
28+
// C++17: Use direct value
29+
xarray single = xarray::of(42);
30+
#else
31+
// Pre-C++17: Use xnode objects
32+
xarray single = xarray::of(xnode::value_of(42));
33+
#endif
34+
Assert(single.size() == 1, "Single element array size should be 1");
35+
Assert(single[0].get_as<int>() == 42, "Single element should be 42");
36+
37+
// Multiple elements of the same type (using of_nodes which works on all standards)
38+
xarray multi = xarray::of_nodes(
39+
xnode::value_of(10),
40+
xnode::value_of(20),
41+
xnode::value_of(30)
42+
);
43+
Assert(multi.size() == 3, "Multi-element array size should be 3");
44+
Assert(multi[0].get_as<int>() == 10, "First element should be 10");
45+
Assert(multi[1].get_as<int>() == 20, "Second element should be 20");
46+
Assert(multi[2].get_as<int>() == 30, "Third element should be 30");
47+
48+
// Different types
49+
#if __cplusplus >= 201703L
50+
// C++17: Use direct values
51+
xarray mixed = xarray::of(10, "test", true, 3.14);
52+
#else
53+
// Pre-C++17: Use xnode objects
54+
xarray mixed = xarray::of(
55+
xnode::value_of(10),
56+
xnode::value_of("test"),
57+
xnode::value_of(true),
58+
xnode::value_of(3.14)
59+
);
60+
#endif
61+
Assert(mixed.size() == 4, "Mixed type array size should be 4");
62+
Assert(mixed[0].is<int>(), "First element should be int");
63+
Assert(mixed[1].is<std::string>(), "Second element should be string");
64+
Assert(mixed[2].is<bool>(), "Third element should be bool");
65+
Assert(mixed[3].is<double>(), "Fourth element should be double");
66+
}
67+
68+
void TestArrayOfWithNestedStructures() {
69+
// Create an object
70+
xobject person;
71+
person.put("name", xnode::value_of("John Doe"));
72+
person.put("age", xnode::value_of(30));
73+
74+
// Create a nested array
75+
xarray hobbies = xarray::of(
76+
xnode::value_of("reading"),
77+
xnode::value_of("coding"),
78+
xnode::value_of("hiking")
79+
);
80+
81+
// Create main array with nested structures
82+
xarray data = xarray::of(
83+
xnode::value_of(person),
84+
xnode::value_of(hobbies),
85+
xnode::value_of(42)
86+
);
87+
88+
// Verify the structure
89+
Assert(data.size() == 3, "Main array size should be 3");
90+
Assert(data[0].is<xobject>(), "First element should be xobject");
91+
Assert(data[1].is<xarray>(), "Second element should be xarray");
92+
Assert(data[2].is<int>(), "Third element should be int");
93+
94+
// Verify nested object
95+
Assert(data[0].get_ptr<xobject>()->contains("name"), "Person object should contain name");
96+
Assert(data[0].get_ptr<xobject>()->get("name").get_as<std::string>() == "John Doe",
97+
"Person name should be John Doe");
98+
99+
// Verify nested array
100+
Assert(data[1].get_ptr<xarray>()->size() == 3, "Hobbies array should have 3 elements");
101+
Assert(data[1].get_ptr<xarray>()->at(0).get_as<std::string>() == "reading",
102+
"First hobby should be reading");
103+
}
104+
105+
void TestArrayOfIterations() {
106+
// Test iteration over array created with "of"
107+
xarray numbers = xarray::of(
108+
xnode::value_of(10),
109+
xnode::value_of(20),
110+
xnode::value_of(30),
111+
xnode::value_of(40)
112+
);
113+
114+
// Range-based for loop
115+
int sum = 0;
116+
for (const auto& item : numbers) {
117+
sum += item.get_as<int>();
118+
}
119+
Assert(sum == 100, "Sum of elements should be 100");
120+
121+
// STL algorithms with array created by "of"
122+
int product = 1;
123+
for (auto it = numbers.begin(); it != numbers.end(); ++it) {
124+
product *= it->get_as<int>();
125+
}
126+
Assert(product == 240000, "Product of elements should be 240000");
127+
}
128+
129+
int xarray_of_test() {
130+
TEST_PROLOG();
131+
TEST_FUNC(ArrayOf);
132+
TEST_FUNC(ArrayOfWithNestedStructures);
133+
TEST_FUNC(ArrayOfIterations);
134+
TEST_EPILOG();
135+
}
136+
137+
int main() {
138+
return xarray_of_test();
139+
}

0 commit comments

Comments
 (0)