Skip to content

Commit 224b924

Browse files
committed
Add optional interop headers
Those headers are used for interoping with various popular C++ libraries. For now, those libraries are Eigen, GLM, HandmadeMath, Raylib, and SFML. Some other ones may be added in the future. Those are disabled by default, as most projects I would imagine would not use them. But in case you need any of them for special reasons like I did, they are there. Signed-off-by: Slendi <slendi@socopon.com>
1 parent 2a161e3 commit 224b924

8 files changed

Lines changed: 583 additions & 14 deletions

File tree

CMakeLists.txt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ option(SMATH_BUILD_EXAMPLES "Build example programs" ON)
88
option(SMATH_BUILD_TESTS "Build unit tests" ON)
99
option(SMATH_BUILD_MODULES "Enable C++20 modules support" OFF)
1010
option(SMATH_ENABLE_LEGACY_HEADER "Install legacy include <smath.hpp> shim" OFF)
11+
option(SMATH_INSTALL_INTEROP_HEADERS "Install interop headers" OFF)
1112

1213
if(SMATH_BUILD_MODULES)
1314
message(STATUS "Building smath C++ module")
@@ -27,7 +28,11 @@ include(CMakePackageConfigHelpers)
2728
install(TARGETS smath
2829
EXPORT smathTargets
2930
)
30-
install(DIRECTORY include/ DESTINATION include)
31+
install(FILES include/smath/smath.hpp DESTINATION include/smath)
32+
33+
if(SMATH_INSTALL_INTEROP_HEADERS)
34+
install(DIRECTORY include/smath/interop DESTINATION include/smath)
35+
endif()
3136

3237
if(SMATH_ENABLE_LEGACY_HEADER)
3338
file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/include")

README.md

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,26 @@
1212
</a>
1313
</p>
1414

15+
## Features
16+
17+
- Generic `Vec<N, T>` class with useful aliases `Vec2/Vec3/Vec4` and friendly accessors (`x/y/z/w`, `r/g/b/a`). They support approx-equal and tuple/structured bindings.
18+
- `std::format` support.
19+
- Compile-time swizzles via `swizzle<"...">`.
20+
- Generic matrix `Mat` class with useful aliases `Mat2/Mat3/Mat4`.
21+
- `Quaternion<T>` built on `Vec4`.
22+
- Angle helpers `rad/deg/turns` respecting a configurable base unit via the macro `SMATH_ANGLE_UNIT`.
23+
- Optional implicit conversions.
24+
- Packing utilities for normalized RGBA (`pack_unorm4x8`, `unpack_snorm4x8`, etc.).
25+
- C++20 modules support.
26+
- Built-in interop adapter and optional interop headers for various libraries.
27+
1528
## Quick Start
1629

1730
Create `main.cpp`:
1831

1932
```cpp
2033
#include <print>
21-
#include <smath.hpp>
34+
#include <smath/smath.hpp>
2235

2336
int main() {
2437
using namespace smath;
@@ -39,17 +52,20 @@ g++ -std=c++23 -Iinclude main.cpp -o quickstart
3952
./quickstart
4053
```
4154

42-
## Features
55+
## Interop Headers
4356

44-
- Generic `Vec<N, T>` class with useful aliases `Vec2/Vec3/Vec4` and friendly accessors (`x/y/z/w`, `r/g/b/a`). They support approx-equal and tuple/structured bindings.
45-
- `std::format` support.
46-
- Compile-time swizzles via `swizzle<"...">`.
47-
- Generic matrix `Mat` class with useful aliases `Mat2/Mat3/Mat4`.
48-
- `Quaternion<T>` built on `Vec4`.
49-
- Angle helpers `rad/deg/turns` respecting a configurable base unit via the macro `SMATH_ANGLE_UNIT`.
50-
- Optional implicit conversions.
51-
- Packing utilities for normalized RGBA (`pack_unorm4x8`, `unpack_snorm4x8`, etc.).
52-
- C++20 modules support.
57+
smath ships optional interop adapters under `include/smath/interop/`.
58+
59+
When installing with CMake, interop headers are gated behind:
60+
61+
- `-DSMATH_INSTALL_INTEROP_HEADERS=ON`
62+
63+
All of them plug into the same generic API defined in `smath.hpp`:
64+
65+
```cpp
66+
auto v = smath::from_external(external_value);
67+
auto ext = smath::to_external<ExternalType>(smath_value);
68+
```
5369

5470
## License
5571

flake.nix

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,10 +55,14 @@
5555
})
5656
];
5757

58+
extraCMakeFlags = [
59+
"-DSMATH_INSTALL_INTEROP_HEADERS=ON"
60+
];
61+
5862
installPhase = ''
5963
runHook preInstall
60-
mkdir -p $out/include/smath
61-
cp ../include/smath/smath.hpp $out/include/smath/
64+
mkdir -p $out/include/
65+
cp -r ../include/smath/ $out/include/
6266
runHook postInstall
6367
'';
6468

include/smath/interop/eigen.hpp

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
/*
2+
* smath - Single-file linear algebra math library for C++23.
3+
*
4+
* Copyright 2025 Slendi <slendi@socopon.com>
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
#pragma once
20+
21+
#include <Eigen/Core>
22+
#include <Eigen/Geometry>
23+
24+
#include <smath/smath.hpp>
25+
26+
namespace smath
27+
{
28+
29+
template<class Scalar, int N, int Options, int MaxRows, int MaxCols>
30+
requires(std::is_arithmetic_v<Scalar> && (N > 0))
31+
struct interop_adapter<Eigen::Matrix<Scalar, N, 1, Options, MaxRows, MaxCols>>
32+
{
33+
using smath_type = Vec<static_cast<std::size_t>(N), Scalar>;
34+
35+
static constexpr auto to_smath(
36+
Eigen::Matrix<Scalar, N, 1, Options, MaxRows, MaxCols> const &v)
37+
-> smath_type
38+
{
39+
smath_type out {};
40+
for (std::size_t i = 0; i < static_cast<std::size_t>(N); ++i)
41+
out[i] = v(static_cast<int>(i));
42+
return out;
43+
}
44+
45+
static constexpr auto from_smath(smath_type const &v)
46+
-> Eigen::Matrix<Scalar, N, 1, Options, MaxRows, MaxCols>
47+
{
48+
Eigen::Matrix<Scalar, N, 1, Options, MaxRows, MaxCols> out;
49+
for (std::size_t i = 0; i < static_cast<std::size_t>(N); ++i)
50+
out(static_cast<int>(i)) = v[i];
51+
return out;
52+
}
53+
};
54+
55+
template<class Scalar, int N, int Options, int MaxRows, int MaxCols>
56+
requires(std::is_arithmetic_v<Scalar> && (N > 0))
57+
struct interop_adapter<Eigen::Matrix<Scalar, 1, N, Options, MaxRows, MaxCols>>
58+
{
59+
using smath_type = Vec<static_cast<std::size_t>(N), Scalar>;
60+
61+
static constexpr auto to_smath(
62+
Eigen::Matrix<Scalar, 1, N, Options, MaxRows, MaxCols> const &v)
63+
-> smath_type
64+
{
65+
smath_type out {};
66+
for (std::size_t i = 0; i < static_cast<std::size_t>(N); ++i)
67+
out[i] = v(static_cast<int>(i));
68+
return out;
69+
}
70+
71+
static constexpr auto from_smath(smath_type const &v)
72+
-> Eigen::Matrix<Scalar, 1, N, Options, MaxRows, MaxCols>
73+
{
74+
Eigen::Matrix<Scalar, 1, N, Options, MaxRows, MaxCols> out;
75+
for (std::size_t i = 0; i < static_cast<std::size_t>(N); ++i)
76+
out(static_cast<int>(i)) = v[i];
77+
return out;
78+
}
79+
};
80+
81+
template<class Scalar, int R, int C, int Options, int MaxRows, int MaxCols>
82+
requires(std::is_arithmetic_v<Scalar> && (R > 1) && (C > 1))
83+
struct interop_adapter<Eigen::Matrix<Scalar, R, C, Options, MaxRows, MaxCols>>
84+
{
85+
using smath_type
86+
= Mat<static_cast<std::size_t>(R), static_cast<std::size_t>(C), Scalar>;
87+
88+
static constexpr auto to_smath(
89+
Eigen::Matrix<Scalar, R, C, Options, MaxRows, MaxCols> const &m)
90+
-> smath_type
91+
{
92+
smath_type out {};
93+
for (std::size_t c = 0; c < static_cast<std::size_t>(C); ++c) {
94+
for (std::size_t r = 0; r < static_cast<std::size_t>(R); ++r)
95+
out[r, c] = m(static_cast<int>(r), static_cast<int>(c));
96+
}
97+
return out;
98+
}
99+
100+
static constexpr auto from_smath(smath_type const &m)
101+
-> Eigen::Matrix<Scalar, R, C, Options, MaxRows, MaxCols>
102+
{
103+
Eigen::Matrix<Scalar, R, C, Options, MaxRows, MaxCols> out;
104+
for (std::size_t c = 0; c < static_cast<std::size_t>(C); ++c) {
105+
for (std::size_t r = 0; r < static_cast<std::size_t>(R); ++r)
106+
out(static_cast<int>(r), static_cast<int>(c)) = m[r, c];
107+
}
108+
return out;
109+
}
110+
};
111+
112+
template<class Scalar, int Options>
113+
requires std::is_arithmetic_v<Scalar>
114+
struct interop_adapter<Eigen::Quaternion<Scalar, Options>>
115+
{
116+
using smath_type = Quaternion<Scalar>;
117+
118+
static constexpr auto to_smath(Eigen::Quaternion<Scalar, Options> const &q)
119+
-> smath_type
120+
{
121+
return { q.x(), q.y(), q.z(), q.w() };
122+
}
123+
124+
static constexpr auto from_smath(smath_type const &q)
125+
-> Eigen::Quaternion<Scalar, Options>
126+
{
127+
return { q.w(), q.x(), q.y(), q.z() };
128+
}
129+
};
130+
131+
} // namespace smath

include/smath/interop/glm.hpp

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
/*
2+
* smath - Single-file linear algebra math library for C++23.
3+
*
4+
* Copyright 2025 Slendi <slendi@socopon.com>
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
#pragma once
20+
21+
#include <glm/glm.hpp>
22+
#include <glm/gtc/quaternion.hpp>
23+
24+
#include <smath/smath.hpp>
25+
26+
namespace smath
27+
{
28+
29+
template<glm::length_t L, class T, glm::qualifier Q>
30+
requires std::is_arithmetic_v<T>
31+
struct interop_adapter<glm::vec<L, T, Q>>
32+
{
33+
using smath_type = Vec<static_cast<std::size_t>(L), T>;
34+
35+
static constexpr auto to_smath(glm::vec<L, T, Q> const &v) -> smath_type
36+
{
37+
smath_type out {};
38+
for (std::size_t i = 0; i < static_cast<std::size_t>(L); ++i)
39+
out[i] = v[static_cast<glm::length_t>(i)];
40+
return out;
41+
}
42+
43+
static constexpr auto from_smath(smath_type const &v) -> glm::vec<L, T, Q>
44+
{
45+
glm::vec<L, T, Q> out {};
46+
for (std::size_t i = 0; i < static_cast<std::size_t>(L); ++i)
47+
out[static_cast<glm::length_t>(i)] = v[i];
48+
return out;
49+
}
50+
};
51+
52+
template<glm::length_t C, glm::length_t R, class T, glm::qualifier Q>
53+
requires std::is_arithmetic_v<T>
54+
struct interop_adapter<glm::mat<C, R, T, Q>>
55+
{
56+
using smath_type
57+
= Mat<static_cast<std::size_t>(R), static_cast<std::size_t>(C), T>;
58+
59+
static constexpr auto to_smath(glm::mat<C, R, T, Q> const &m) -> smath_type
60+
{
61+
smath_type out {};
62+
for (std::size_t c = 0; c < static_cast<std::size_t>(C); ++c) {
63+
for (std::size_t r = 0; r < static_cast<std::size_t>(R); ++r) {
64+
out[r, c] = m[static_cast<glm::length_t>(c)]
65+
[static_cast<glm::length_t>(r)];
66+
}
67+
}
68+
return out;
69+
}
70+
71+
static constexpr auto from_smath(smath_type const &m)
72+
-> glm::mat<C, R, T, Q>
73+
{
74+
glm::mat<C, R, T, Q> out {};
75+
for (std::size_t c = 0; c < static_cast<std::size_t>(C); ++c) {
76+
for (std::size_t r = 0; r < static_cast<std::size_t>(R); ++r) {
77+
out[static_cast<glm::length_t>(c)]
78+
[static_cast<glm::length_t>(r)]
79+
= m[r, c];
80+
}
81+
}
82+
return out;
83+
}
84+
};
85+
86+
template<class T, glm::qualifier Q>
87+
requires std::is_arithmetic_v<T>
88+
struct interop_adapter<glm::qua<T, Q>>
89+
{
90+
using smath_type = Quaternion<T>;
91+
92+
static constexpr auto to_smath(glm::qua<T, Q> const &q) -> smath_type
93+
{
94+
return { q.x, q.y, q.z, q.w };
95+
}
96+
97+
static constexpr auto from_smath(smath_type const &q) -> glm::qua<T, Q>
98+
{
99+
return { q.w(), q.x(), q.y(), q.z() };
100+
}
101+
};
102+
103+
} // namespace smath

0 commit comments

Comments
 (0)