Skip to content

Commit 648f439

Browse files
committed
Use std::optional<void*> for custom_data instead of raw pointer
Replace void* with nullptr pattern with std::optional<void*> for custom_data in the Form class and assembly functions. This is the canonical modern C++ approach for representing optional values. Changes: - Form.h: integral_data::custom_data now uses std::optional<void*> - Form.h: custom_data() returns std::optional<void*> - Form.h: set_custom_data() accepts std::optional<void*> - assemble_*_impl.h: Function parameters use std::optional<void*> - assemble_*_impl.h: Kernel calls use .value_or(nullptr) - Python bindings: Handle std::optional with None support - Test: Update to expect None instead of 0 for unset custom_data Benefits: - Clearer intent: "optional value" vs "magic nullptr" - Type safety: Distinguishes "no value" from "null pointer value" - Pythonic: Returns None instead of 0 when not set - Zero-cost: .value_or(nullptr) compiles to same code
1 parent fa84113 commit 648f439

File tree

6 files changed

+66
-51
lines changed

6 files changed

+66
-51
lines changed

cpp/dolfinx/fem/Form.h

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ struct integral_data
6767
and std::is_convertible_v<std::remove_cvref_t<W>,
6868
std::vector<int>>
6969
integral_data(K&& kernel, V&& entities, W&& coeffs,
70-
void* custom_data = nullptr)
70+
std::optional<void*> custom_data = std::nullopt)
7171
: kernel(std::forward<K>(kernel)), entities(std::forward<V>(entities)),
7272
coeffs(std::forward<W>(coeffs)), custom_data(custom_data)
7373
{
@@ -89,7 +89,7 @@ struct integral_data
8989
/// @brief Custom user data pointer passed to the kernel function.
9090
/// This can be used to pass runtime-computed data (e.g., per-cell
9191
/// quadrature rules, material properties) to the kernel.
92-
void* custom_data = nullptr;
92+
std::optional<void*> custom_data = std::nullopt;
9393
};
9494

9595
/// @brief A representation of finite element variational forms.
@@ -409,8 +409,8 @@ class Form
409409
/// @param[in] id Integral subdomain ID.
410410
/// @param[in] kernel_idx Index of the kernel (we may have multiple
411411
/// kernels for a given ID in mixed-topology meshes).
412-
/// @return Custom data pointer for the integral, or nullptr if not set.
413-
void* custom_data(IntegralType type, int id, int kernel_idx) const
412+
/// @return Custom data pointer for the integral, or std::nullopt if not set.
413+
std::optional<void*> custom_data(IntegralType type, int id, int kernel_idx) const
414414
{
415415
auto it = _integrals.find({type, id, kernel_idx});
416416
if (it == _integrals.end())
@@ -423,8 +423,9 @@ class Form
423423
/// @param[in] type Integral type.
424424
/// @param[in] id Integral subdomain ID.
425425
/// @param[in] kernel_idx Index of the kernel.
426-
/// @param[in] data Custom data pointer to set.
427-
void set_custom_data(IntegralType type, int id, int kernel_idx, void* data)
426+
/// @param[in] data Custom data pointer to set, or std::nullopt to clear.
427+
void set_custom_data(IntegralType type, int id, int kernel_idx,
428+
std::optional<void*> data)
428429
{
429430
auto it = _integrals.find({type, id, kernel_idx});
430431
if (it == _integrals.end())

cpp/dolfinx/fem/assemble_matrix_impl.h

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include <dolfinx/mesh/Topology.h>
1919
#include <functional>
2020
#include <iterator>
21+
#include <optional>
2122
#include <span>
2223
#include <tuple>
2324
#include <vector>
@@ -75,7 +76,8 @@ void assemble_cells_matrix(
7576
std::span<const std::int8_t> bc1, FEkernel<T> auto kernel,
7677
md::mdspan<const T, md::dextents<std::size_t, 2>> coeffs,
7778
std::span<const T> constants, std::span<const std::uint32_t> cell_info0,
78-
std::span<const std::uint32_t> cell_info1, void* custom_data = nullptr)
79+
std::span<const std::uint32_t> cell_info1,
80+
std::optional<void*> custom_data = std::nullopt)
7981
{
8082
if (cells.empty())
8183
return;
@@ -110,7 +112,7 @@ void assemble_cells_matrix(
110112
// Tabulate tensor
111113
std::ranges::fill(Ae, 0);
112114
kernel(Ae.data(), &coeffs(c, 0), constants.data(), cdofs.data(), nullptr,
113-
nullptr, custom_data);
115+
nullptr, custom_data.value_or(nullptr));
114116

115117
// Compute A = P_0 \tilde{A} P_1^T (dof transformation)
116118
P0(Ae, cell_info0, cell0, ndim1); // B = P0 \tilde{A}
@@ -224,7 +226,7 @@ void assemble_entities(
224226
std::span<const T> constants, std::span<const std::uint32_t> cell_info0,
225227
std::span<const std::uint32_t> cell_info1,
226228
md::mdspan<const std::uint8_t, md::dextents<std::size_t, 2>> perms,
227-
void* custom_data = nullptr)
229+
std::optional<void*> custom_data = std::nullopt)
228230
{
229231
if (entities.empty())
230232
return;
@@ -262,7 +264,7 @@ void assemble_entities(
262264
// Tabulate tensor
263265
std::ranges::fill(Ae, 0);
264266
kernel(Ae.data(), &coeffs(f, 0), constants.data(), cdofs.data(),
265-
&local_entity, &perm, custom_data);
267+
&local_entity, &perm, custom_data.value_or(nullptr));
266268
P0(Ae, cell_info0, cell0, ndim1);
267269
P1T(Ae, cell_info1, cell1, ndim0);
268270

@@ -367,7 +369,7 @@ void assemble_interior_facets(
367369
std::span<const T> constants, std::span<const std::uint32_t> cell_info0,
368370
std::span<const std::uint32_t> cell_info1,
369371
md::mdspan<const std::uint8_t, md::dextents<std::size_t, 2>> perms,
370-
void* custom_data = nullptr)
372+
std::optional<void*> custom_data = std::nullopt)
371373
{
372374
if (facets.empty())
373375
return;
@@ -444,7 +446,7 @@ void assemble_interior_facets(
444446
: std::array{perms(cells[0], local_facet[0]),
445447
perms(cells[1], local_facet[1])};
446448
kernel(Ae.data(), &coeffs(f, 0, 0), constants.data(), cdofs.data(),
447-
local_facet.data(), perm.data(), custom_data);
449+
local_facet.data(), perm.data(), custom_data.value_or(nullptr));
448450

449451
// Local element layout is a 2x2 block matrix with structure
450452
//
@@ -609,7 +611,8 @@ void assemble_matrix(
609611
std::span cells0 = a.domain_arg(IntegralType::cell, 0, i, cell_type_idx);
610612
std::span cells1 = a.domain_arg(IntegralType::cell, 1, i, cell_type_idx);
611613
auto& [coeffs, cstride] = coefficients.at({IntegralType::cell, i});
612-
void* custom_data = a.custom_data(IntegralType::cell, i, cell_type_idx);
614+
std::optional<void*> custom_data
615+
= a.custom_data(IntegralType::cell, i, cell_type_idx);
613616
assert(cells.size() * cstride == coeffs.size());
614617
impl::assemble_cells_matrix(
615618
mat_set, x_dofmap, x, cells, {dofs0, bs0, cells0}, P0,
@@ -651,7 +654,8 @@ void assemble_matrix(
651654
assert(fn);
652655
auto& [coeffs, cstride]
653656
= coefficients.at({IntegralType::interior_facet, i});
654-
void* custom_data = a.custom_data(IntegralType::interior_facet, i, 0);
657+
std::optional<void*> custom_data
658+
= a.custom_data(IntegralType::interior_facet, i, 0);
655659

656660
std::span facets = a.domain(IntegralType::interior_facet, i, 0);
657661
std::span facets0 = a.domain_arg(IntegralType::interior_facet, 0, i, 0);
@@ -694,7 +698,7 @@ void assemble_matrix(
694698
auto fn = a.kernel(itg_type, i, 0);
695699
assert(fn);
696700
auto& [coeffs, cstride] = coefficients.at({itg_type, i});
697-
void* custom_data = a.custom_data(itg_type, i, 0);
701+
std::optional<void*> custom_data = a.custom_data(itg_type, i, 0);
698702

699703
std::span e = a.domain(itg_type, i, 0);
700704
mdspanx2_t entities(e.data(), e.size() / 2, 2);

cpp/dolfinx/fem/assemble_scalar_impl.h

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include <dolfinx/mesh/Mesh.h>
1818
#include <dolfinx/mesh/Topology.h>
1919
#include <memory>
20+
#include <optional>
2021
#include <vector>
2122

2223
namespace dolfinx::fem::impl
@@ -31,7 +32,7 @@ T assemble_cells(mdspan2_t x_dofmap,
3132
std::span<const T> constants,
3233
md::mdspan<const T, md::dextents<std::size_t, 2>> coeffs,
3334
std::span<scalar_value_t<T>> cdofs_b,
34-
void* custom_data = nullptr)
35+
std::optional<void*> custom_data = std::nullopt)
3536
{
3637
T value(0);
3738
if (cells.empty())
@@ -50,7 +51,7 @@ T assemble_cells(mdspan2_t x_dofmap,
5051
std::copy_n(&x(x_dofs[i], 0), 3, std::next(cdofs_b.begin(), 3 * i));
5152

5253
fn(&value, &coeffs(index, 0), constants.data(), cdofs_b.data(), nullptr,
53-
nullptr, custom_data);
54+
nullptr, custom_data.value_or(nullptr));
5455
}
5556

5657
return value;
@@ -78,7 +79,7 @@ T assemble_entities(
7879
FEkernel<T> auto fn, std::span<const T> constants,
7980
md::mdspan<const T, md::dextents<std::size_t, 2>> coeffs,
8081
md::mdspan<const std::uint8_t, md::dextents<std::size_t, 2>> perms,
81-
std::span<scalar_value_t<T>> cdofs_b, void* custom_data = nullptr)
82+
std::span<scalar_value_t<T>> cdofs_b, std::optional<void*> custom_data = std::nullopt)
8283
{
8384
T value(0);
8485
if (entities.empty())
@@ -100,7 +101,7 @@ T assemble_entities(
100101
// Permutations
101102
std::uint8_t perm = perms.empty() ? 0 : perms(cell, local_entity);
102103
fn(&value, &coeffs(f, 0), constants.data(), cdofs_b.data(), &local_entity,
103-
&perm, custom_data);
104+
&perm, custom_data.value_or(nullptr));
104105
}
105106

106107
return value;
@@ -121,7 +122,7 @@ T assemble_interior_facets(
121122
md::dynamic_extent>>
122123
coeffs,
123124
md::mdspan<const std::uint8_t, md::dextents<std::size_t, 2>> perms,
124-
std::span<scalar_value_t<T>> cdofs_b, void* custom_data = nullptr)
125+
std::span<scalar_value_t<T>> cdofs_b, std::optional<void*> custom_data = std::nullopt)
125126
{
126127
T value(0);
127128
if (facets.empty())
@@ -151,7 +152,7 @@ T assemble_interior_facets(
151152
: std::array{perms(cells[0], local_facet[0]),
152153
perms(cells[1], local_facet[1])};
153154
fn(&value, &coeffs(f, 0, 0), constants.data(), cdofs_b.data(),
154-
local_facet.data(), perm.data(), custom_data);
155+
local_facet.data(), perm.data(), custom_data.value_or(nullptr));
155156
}
156157

157158
return value;
@@ -179,7 +180,7 @@ T assemble_scalar(
179180
auto fn = M.kernel(IntegralType::cell, i, 0);
180181
assert(fn);
181182
auto& [coeffs, cstride] = coefficients.at({IntegralType::cell, i});
182-
void* custom_data = M.custom_data(IntegralType::cell, i, 0);
183+
std::optional<void*> custom_data = M.custom_data(IntegralType::cell, i, 0);
183184
std::span<const std::int32_t> cells = M.domain(IntegralType::cell, i, 0);
184185
assert(cells.size() * cstride == coeffs.size());
185186
value += impl::assemble_cells(
@@ -206,7 +207,7 @@ T assemble_scalar(
206207
assert(fn);
207208
auto& [coeffs, cstride]
208209
= coefficients.at({IntegralType::interior_facet, i});
209-
void* custom_data = M.custom_data(IntegralType::interior_facet, i, 0);
210+
std::optional<void*> custom_data = M.custom_data(IntegralType::interior_facet, i, 0);
210211
std::span facets = M.domain(IntegralType::interior_facet, i, 0);
211212

212213
constexpr std::size_t num_adjacent_cells = 2;
@@ -239,7 +240,7 @@ T assemble_scalar(
239240
auto fn = M.kernel(itg_type, i, 0);
240241
assert(fn);
241242
auto& [coeffs, cstride] = coefficients.at({itg_type, i});
242-
void* custom_data = M.custom_data(itg_type, i, 0);
243+
std::optional<void*> custom_data = M.custom_data(itg_type, i, 0);
243244

244245
std::span entities = M.domain(itg_type, i, 0);
245246

cpp/dolfinx/fem/assemble_vector_impl.h

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ void _lift_bc_cells(
9393
std::span<const std::uint32_t> cell_info0,
9494
std::span<const std::uint32_t> cell_info1, std::span<const T> bc_values1,
9595
std::span<const std::int8_t> bc_markers1, std::span<const T> x0, T alpha,
96-
void* custom_data = nullptr)
96+
std::optional<void*> custom_data = std::nullopt)
9797
{
9898
if (cells.empty())
9999
return;
@@ -165,7 +165,7 @@ void _lift_bc_cells(
165165

166166
std::ranges::fill(Ae, 0);
167167
kernel(Ae.data(), &coeffs(index, 0), constants.data(), cdofs.data(),
168-
nullptr, nullptr, custom_data);
168+
nullptr, nullptr, custom_data.value_or(nullptr));
169169
P0(Ae, cell_info0, c0, num_cols);
170170
P1T(Ae, cell_info1, c1, num_rows);
171171

@@ -288,7 +288,7 @@ void _lift_bc_entities(
288288
std::span<const std::uint32_t> cell_info1, std::span<const T> bc_values1,
289289
std::span<const std::int8_t> bc_markers1, std::span<const T> x0, T alpha,
290290
md::mdspan<const std::uint8_t, md::dextents<std::size_t, 2>> perms,
291-
void* custom_data = nullptr)
291+
std::optional<void*> custom_data = std::nullopt)
292292
{
293293
if (entities.empty())
294294
return;
@@ -347,7 +347,7 @@ void _lift_bc_entities(
347347
std::uint8_t perm = perms.empty() ? 0 : perms(cell, local_entity);
348348
std::ranges::fill(Ae, 0);
349349
kernel(Ae.data(), &coeffs(index, 0), constants.data(), cdofs.data(),
350-
&local_entity, &perm, custom_data);
350+
&local_entity, &perm, custom_data.value_or(nullptr));
351351
P0(Ae, cell_info0, cell0, num_cols);
352352
P1T(Ae, cell_info1, cell1, num_rows);
353353

@@ -446,7 +446,7 @@ void _lift_bc_interior_facets(
446446
std::span<const std::uint32_t> cell_info1, std::span<const T> bc_values1,
447447
std::span<const std::int8_t> bc_markers1, std::span<const T> x0, T alpha,
448448
md::mdspan<const std::uint8_t, md::dextents<std::size_t, 2>> perms,
449-
void* custom_data = nullptr)
449+
std::optional<void*> custom_data = std::nullopt)
450450
{
451451
if (facets.empty())
452452
return;
@@ -562,7 +562,7 @@ void _lift_bc_interior_facets(
562562
: std::array{perms(cells[0], local_facet[0]),
563563
perms(cells[1], local_facet[1])};
564564
kernel(Ae.data(), &coeffs(f, 0, 0), constants.data(), cdofs.data(),
565-
local_facet.data(), perm.data(), custom_data);
565+
local_facet.data(), perm.data(), custom_data.value_or(nullptr));
566566

567567
if (cells0[0] >= 0)
568568
P0(Ae, cell_info0, cells0[0], num_cols);
@@ -674,7 +674,7 @@ void assemble_cells(
674674
std::tuple<mdspan2_t, int, std::span<const std::int32_t>> dofmap,
675675
FEkernel<T> auto kernel, std::span<const T> constants,
676676
md::mdspan<const T, md::dextents<std::size_t, 2>> coeffs,
677-
std::span<const std::uint32_t> cell_info0, void* custom_data = nullptr)
677+
std::span<const std::uint32_t> cell_info0, std::optional<void*> custom_data = std::nullopt)
678678
{
679679
if (cells.empty())
680680
return;
@@ -701,7 +701,7 @@ void assemble_cells(
701701
// Tabulate vector for cell
702702
std::ranges::fill(be, 0);
703703
kernel(be.data(), &coeffs(index, 0), constants.data(), cdofs.data(),
704-
nullptr, nullptr, custom_data);
704+
nullptr, nullptr, custom_data.value_or(nullptr));
705705
P0(be, cell_info0, c0, 1);
706706

707707
// Scatter cell vector to 'global' vector array
@@ -773,7 +773,7 @@ void assemble_entities(
773773
md::mdspan<const T, md::dextents<std::size_t, 2>> coeffs,
774774
std::span<const std::uint32_t> cell_info0,
775775
md::mdspan<const std::uint8_t, md::dextents<std::size_t, 2>> perms,
776-
void* custom_data = nullptr)
776+
std::optional<void*> custom_data = std::nullopt)
777777
{
778778
if (entities.empty())
779779
return;
@@ -805,7 +805,7 @@ void assemble_entities(
805805
// Tabulate element vector
806806
std::ranges::fill(be, 0);
807807
kernel(be.data(), &coeffs(f, 0), constants.data(), cdofs.data(),
808-
&local_entity, &perm, custom_data);
808+
&local_entity, &perm, custom_data.value_or(nullptr));
809809
P0(be, cell_info0, cell0, 1);
810810

811811
// Add element vector to global vector
@@ -871,7 +871,7 @@ void assemble_interior_facets(
871871
coeffs,
872872
std::span<const std::uint32_t> cell_info0,
873873
md::mdspan<const std::uint8_t, md::dextents<std::size_t, 2>> perms,
874-
void* custom_data = nullptr)
874+
std::optional<void*> custom_data = std::nullopt)
875875
{
876876
using X = scalar_value_t<T>;
877877

@@ -923,7 +923,7 @@ void assemble_interior_facets(
923923
: std::array{perms(cells[0], local_facet[0]),
924924
perms(cells[1], local_facet[1])};
925925
kernel(be.data(), &coeffs(f, 0, 0), constants.data(), cdofs.data(),
926-
local_facet.data(), perm.data(), custom_data);
926+
local_facet.data(), perm.data(), custom_data.value_or(nullptr));
927927

928928
if (cells0[0] >= 0)
929929
P0(be, cell_info0, cells0[0], 1);
@@ -1031,7 +1031,7 @@ void lift_bc(V&& b, const Form<T, U>& a, mdspan2_t x_dofmap,
10311031
auto kernel = a.kernel(IntegralType::cell, i, 0);
10321032
assert(kernel);
10331033
auto& [_coeffs, cstride] = coefficients.at({IntegralType::cell, i});
1034-
void* custom_data = a.custom_data(IntegralType::cell, i, 0);
1034+
std::optional<void*> custom_data = a.custom_data(IntegralType::cell, i, 0);
10351035
std::span cells = a.domain(IntegralType::cell, i, 0);
10361036
std::span cells0 = a.domain_arg(IntegralType::cell, 0, i, 0);
10371037
std::span cells1 = a.domain_arg(IntegralType::cell, 1, i, 0);
@@ -1079,7 +1079,7 @@ void lift_bc(V&& b, const Form<T, U>& a, mdspan2_t x_dofmap,
10791079
assert(kernel);
10801080
auto& [coeffs, cstride]
10811081
= coefficients.at({IntegralType::interior_facet, i});
1082-
void* custom_data = a.custom_data(IntegralType::interior_facet, i, 0);
1082+
std::optional<void*> custom_data = a.custom_data(IntegralType::interior_facet, i, 0);
10831083

10841084
using mdspanx22_t
10851085
= md::mdspan<const std::int32_t,
@@ -1114,7 +1114,7 @@ void lift_bc(V&& b, const Form<T, U>& a, mdspan2_t x_dofmap,
11141114
auto kernel = a.kernel(itg_type, i, 0);
11151115
assert(kernel);
11161116
auto& [coeffs, cstride] = coefficients.at({itg_type, i});
1117-
void* custom_data = a.custom_data(itg_type, i, 0);
1117+
std::optional<void*> custom_data = a.custom_data(itg_type, i, 0);
11181118

11191119
using mdspanx2_t
11201120
= md::mdspan<const std::int32_t,
@@ -1286,7 +1286,7 @@ void assemble_vector(
12861286
std::span cells = L.domain(IntegralType::cell, i, cell_type_idx);
12871287
std::span cells0 = L.domain_arg(IntegralType::cell, 0, i, cell_type_idx);
12881288
auto& [coeffs, cstride] = coefficients.at({IntegralType::cell, i});
1289-
void* custom_data = L.custom_data(IntegralType::cell, i, cell_type_idx);
1289+
void* custom_data = L.custom_data(IntegralType::cell, i, cell_type_idx).value_or(nullptr);
12901290
assert(cells.size() * cstride == coeffs.size());
12911291
if (bs == 1)
12921292
{
@@ -1341,7 +1341,7 @@ void assemble_vector(
13411341
assert(fn);
13421342
auto& [coeffs, cstride]
13431343
= coefficients.at({IntegralType::interior_facet, i});
1344-
void* custom_data = L.custom_data(IntegralType::interior_facet, i, 0);
1344+
void* custom_data = L.custom_data(IntegralType::interior_facet, i, 0).value_or(nullptr);
13451345
std::span facets = L.domain(IntegralType::interior_facet, i, 0);
13461346
std::span facets1 = L.domain_arg(IntegralType::interior_facet, 0, i, 0);
13471347
assert((facets.size() / 4) * 2 * cstride == coeffs.size());
@@ -1393,7 +1393,7 @@ void assemble_vector(
13931393
auto fn = L.kernel(itg_type, i, 0);
13941394
assert(fn);
13951395
auto& [coeffs, cstride] = coefficients.at({itg_type, i});
1396-
void* custom_data = L.custom_data(itg_type, i, 0);
1396+
void* custom_data = L.custom_data(itg_type, i, 0).value_or(nullptr);
13971397
std::span e = L.domain(itg_type, i, 0);
13981398
mdspanx2_t entities(e.data(), e.size() / 2, 2);
13991399
std::span e1 = L.domain_arg(itg_type, 0, i, 0);

0 commit comments

Comments
 (0)