Skip to content

Commit cf52114

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 cf52114

File tree

6 files changed

+74
-51
lines changed

6 files changed

+74
-51
lines changed

cpp/dolfinx/fem/Form.h

Lines changed: 8 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,9 @@ 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,
414+
int kernel_idx) const
414415
{
415416
auto it = _integrals.find({type, id, kernel_idx});
416417
if (it == _integrals.end())
@@ -423,8 +424,9 @@ class Form
423424
/// @param[in] type Integral type.
424425
/// @param[in] id Integral subdomain ID.
425426
/// @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)
427+
/// @param[in] data Custom data pointer to set, or std::nullopt to clear.
428+
void set_custom_data(IntegralType type, int id, int kernel_idx,
429+
std::optional<void*> data)
428430
{
429431
auto it = _integrals.find({type, id, kernel_idx});
430432
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: 13 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,8 @@ 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,
83+
std::optional<void*> custom_data = std::nullopt)
8284
{
8385
T value(0);
8486
if (entities.empty())
@@ -100,7 +102,7 @@ T assemble_entities(
100102
// Permutations
101103
std::uint8_t perm = perms.empty() ? 0 : perms(cell, local_entity);
102104
fn(&value, &coeffs(f, 0), constants.data(), cdofs_b.data(), &local_entity,
103-
&perm, custom_data);
105+
&perm, custom_data.value_or(nullptr));
104106
}
105107

106108
return value;
@@ -121,7 +123,8 @@ T assemble_interior_facets(
121123
md::dynamic_extent>>
122124
coeffs,
123125
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)
126+
std::span<scalar_value_t<T>> cdofs_b,
127+
std::optional<void*> custom_data = std::nullopt)
125128
{
126129
T value(0);
127130
if (facets.empty())
@@ -151,7 +154,7 @@ T assemble_interior_facets(
151154
: std::array{perms(cells[0], local_facet[0]),
152155
perms(cells[1], local_facet[1])};
153156
fn(&value, &coeffs(f, 0, 0), constants.data(), cdofs_b.data(),
154-
local_facet.data(), perm.data(), custom_data);
157+
local_facet.data(), perm.data(), custom_data.value_or(nullptr));
155158
}
156159

157160
return value;
@@ -179,7 +182,7 @@ T assemble_scalar(
179182
auto fn = M.kernel(IntegralType::cell, i, 0);
180183
assert(fn);
181184
auto& [coeffs, cstride] = coefficients.at({IntegralType::cell, i});
182-
void* custom_data = M.custom_data(IntegralType::cell, i, 0);
185+
std::optional<void*> custom_data = M.custom_data(IntegralType::cell, i, 0);
183186
std::span<const std::int32_t> cells = M.domain(IntegralType::cell, i, 0);
184187
assert(cells.size() * cstride == coeffs.size());
185188
value += impl::assemble_cells(
@@ -206,7 +209,8 @@ T assemble_scalar(
206209
assert(fn);
207210
auto& [coeffs, cstride]
208211
= coefficients.at({IntegralType::interior_facet, i});
209-
void* custom_data = M.custom_data(IntegralType::interior_facet, i, 0);
212+
std::optional<void*> custom_data
213+
= M.custom_data(IntegralType::interior_facet, i, 0);
210214
std::span facets = M.domain(IntegralType::interior_facet, i, 0);
211215

212216
constexpr std::size_t num_adjacent_cells = 2;
@@ -239,7 +243,7 @@ T assemble_scalar(
239243
auto fn = M.kernel(itg_type, i, 0);
240244
assert(fn);
241245
auto& [coeffs, cstride] = coefficients.at({itg_type, i});
242-
void* custom_data = M.custom_data(itg_type, i, 0);
246+
std::optional<void*> custom_data = M.custom_data(itg_type, i, 0);
243247

244248
std::span entities = M.domain(itg_type, i, 0);
245249

cpp/dolfinx/fem/assemble_vector_impl.h

Lines changed: 22 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,8 @@ 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,
678+
std::optional<void*> custom_data = std::nullopt)
678679
{
679680
if (cells.empty())
680681
return;
@@ -701,7 +702,7 @@ void assemble_cells(
701702
// Tabulate vector for cell
702703
std::ranges::fill(be, 0);
703704
kernel(be.data(), &coeffs(index, 0), constants.data(), cdofs.data(),
704-
nullptr, nullptr, custom_data);
705+
nullptr, nullptr, custom_data.value_or(nullptr));
705706
P0(be, cell_info0, c0, 1);
706707

707708
// Scatter cell vector to 'global' vector array
@@ -773,7 +774,7 @@ void assemble_entities(
773774
md::mdspan<const T, md::dextents<std::size_t, 2>> coeffs,
774775
std::span<const std::uint32_t> cell_info0,
775776
md::mdspan<const std::uint8_t, md::dextents<std::size_t, 2>> perms,
776-
void* custom_data = nullptr)
777+
std::optional<void*> custom_data = std::nullopt)
777778
{
778779
if (entities.empty())
779780
return;
@@ -805,7 +806,7 @@ void assemble_entities(
805806
// Tabulate element vector
806807
std::ranges::fill(be, 0);
807808
kernel(be.data(), &coeffs(f, 0), constants.data(), cdofs.data(),
808-
&local_entity, &perm, custom_data);
809+
&local_entity, &perm, custom_data.value_or(nullptr));
809810
P0(be, cell_info0, cell0, 1);
810811

811812
// Add element vector to global vector
@@ -871,7 +872,7 @@ void assemble_interior_facets(
871872
coeffs,
872873
std::span<const std::uint32_t> cell_info0,
873874
md::mdspan<const std::uint8_t, md::dextents<std::size_t, 2>> perms,
874-
void* custom_data = nullptr)
875+
std::optional<void*> custom_data = std::nullopt)
875876
{
876877
using X = scalar_value_t<T>;
877878

@@ -923,7 +924,7 @@ void assemble_interior_facets(
923924
: std::array{perms(cells[0], local_facet[0]),
924925
perms(cells[1], local_facet[1])};
925926
kernel(be.data(), &coeffs(f, 0, 0), constants.data(), cdofs.data(),
926-
local_facet.data(), perm.data(), custom_data);
927+
local_facet.data(), perm.data(), custom_data.value_or(nullptr));
927928

928929
if (cells0[0] >= 0)
929930
P0(be, cell_info0, cells0[0], 1);
@@ -1031,7 +1032,7 @@ void lift_bc(V&& b, const Form<T, U>& a, mdspan2_t x_dofmap,
10311032
auto kernel = a.kernel(IntegralType::cell, i, 0);
10321033
assert(kernel);
10331034
auto& [_coeffs, cstride] = coefficients.at({IntegralType::cell, i});
1034-
void* custom_data = a.custom_data(IntegralType::cell, i, 0);
1035+
std::optional<void*> custom_data = a.custom_data(IntegralType::cell, i, 0);
10351036
std::span cells = a.domain(IntegralType::cell, i, 0);
10361037
std::span cells0 = a.domain_arg(IntegralType::cell, 0, i, 0);
10371038
std::span cells1 = a.domain_arg(IntegralType::cell, 1, i, 0);
@@ -1079,7 +1080,8 @@ void lift_bc(V&& b, const Form<T, U>& a, mdspan2_t x_dofmap,
10791080
assert(kernel);
10801081
auto& [coeffs, cstride]
10811082
= coefficients.at({IntegralType::interior_facet, i});
1082-
void* custom_data = a.custom_data(IntegralType::interior_facet, i, 0);
1083+
std::optional<void*> custom_data
1084+
= a.custom_data(IntegralType::interior_facet, i, 0);
10831085

10841086
using mdspanx22_t
10851087
= md::mdspan<const std::int32_t,
@@ -1114,7 +1116,7 @@ void lift_bc(V&& b, const Form<T, U>& a, mdspan2_t x_dofmap,
11141116
auto kernel = a.kernel(itg_type, i, 0);
11151117
assert(kernel);
11161118
auto& [coeffs, cstride] = coefficients.at({itg_type, i});
1117-
void* custom_data = a.custom_data(itg_type, i, 0);
1119+
std::optional<void*> custom_data = a.custom_data(itg_type, i, 0);
11181120

11191121
using mdspanx2_t
11201122
= md::mdspan<const std::int32_t,
@@ -1286,7 +1288,8 @@ void assemble_vector(
12861288
std::span cells = L.domain(IntegralType::cell, i, cell_type_idx);
12871289
std::span cells0 = L.domain_arg(IntegralType::cell, 0, i, cell_type_idx);
12881290
auto& [coeffs, cstride] = coefficients.at({IntegralType::cell, i});
1289-
void* custom_data = L.custom_data(IntegralType::cell, i, cell_type_idx);
1291+
void* custom_data = L.custom_data(IntegralType::cell, i, cell_type_idx)
1292+
.value_or(nullptr);
12901293
assert(cells.size() * cstride == coeffs.size());
12911294
if (bs == 1)
12921295
{
@@ -1341,7 +1344,8 @@ void assemble_vector(
13411344
assert(fn);
13421345
auto& [coeffs, cstride]
13431346
= coefficients.at({IntegralType::interior_facet, i});
1344-
void* custom_data = L.custom_data(IntegralType::interior_facet, i, 0);
1347+
void* custom_data
1348+
= L.custom_data(IntegralType::interior_facet, i, 0).value_or(nullptr);
13451349
std::span facets = L.domain(IntegralType::interior_facet, i, 0);
13461350
std::span facets1 = L.domain_arg(IntegralType::interior_facet, 0, i, 0);
13471351
assert((facets.size() / 4) * 2 * cstride == coeffs.size());
@@ -1393,7 +1397,7 @@ void assemble_vector(
13931397
auto fn = L.kernel(itg_type, i, 0);
13941398
assert(fn);
13951399
auto& [coeffs, cstride] = coefficients.at({itg_type, i});
1396-
void* custom_data = L.custom_data(itg_type, i, 0);
1400+
void* custom_data = L.custom_data(itg_type, i, 0).value_or(nullptr);
13971401
std::span e = L.domain(itg_type, i, 0);
13981402
mdspanx2_t entities(e.data(), e.size() / 2, 2);
13991403
std::span e1 = L.domain_arg(itg_type, 0, i, 0);

0 commit comments

Comments
 (0)