Skip to content
Merged
42 changes: 31 additions & 11 deletions .agents/docs/plans/implementation-plan.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
# Primitives 下一步实现计划清单(2026-03-23
# Primitives 下一步实现计划清单(2026-03-26

## 总体原则

- [ ] 先定义稳定 API 边界,再落地实现
- [ ] 每个能力都配套 tests + examples + docs
- [ ] 高风险转换默认禁止隐式触发,只允许显式 API
- [ ] 明确分层职责:`algorithms` 负责值域/排序/边界元信息与值算法;`conversion` 负责显式类型变换
- [ ] 明确依赖方向:`conversion` 可依赖 `algorithms`,`algorithms` 不反向依赖 `conversion`

## M1. C API 互操作层(双向)

Expand All @@ -27,21 +29,30 @@
### 工作清单

- [ ] 扩展 concept 分层(category/representation/policy-capability)
- [ ] 完善 `traits<T>` 元信息(`kind`、`rep_type`、limits、policy tags)
- [ ] 提供检测类 API(可转换性/是否有损/错误模型能力)
- [ ] 明确 `underlying::traits<T>` 与 `algorithms::traits<T>` 的职责边界:前者描述表示层;后者描述值算法层
- [ ] 完善 `traits<T>` 元信息(`kind`、`rep_type`、policy tags)
- [ ] 设计 `algorithms::traits<T>`,内容参考 `std::numeric_limits`,但只保留算法/比较所需子集:
- [ ] `min()` / `lowest()` / `max()` / `epsilon()` / `infinity()` / `quiet_nan()`
- [ ] `is_bounded` / `is_exact` / `is_signed` / `is_integer` / `is_iec559`
- [ ] `has_infinity` / `has_quiet_nan` / `digits` / `digits10` / `radix`
- [ ] 增加排序能力元信息:`comparison_category`、`totally_ordered`、`partially_ordered`、`unordered_possible`
- [ ] 提供双参聚合元信息:`algorithms::common_traits<Lhs, Rhs>`,统一暴露 `common_rep`、可比较性、边界查询与策略兼容性
- [ ] 提供检测类 API(可比较性/可裁剪性/可转换性/是否有损/错误模型能力)
- [ ] 统一 `constexpr` 查询入口,减少分散 traits 访问
- [ ] 增加编译期测试矩阵(`static_assert` 覆盖)

### 验收标准

- [ ] 上层模块仅依赖公开 concept/traits,不依赖 `details::*`
- [ ] 元信息可支撑转换层与算法层的约束判定
- [ ] `algorithms::traits` 可直接支撑算法层判定,并作为 conversion 的元信息依赖
- [ ] `std::numeric_limits` 相关逻辑不再散落在 `conversion`/`operations` 内部

## M3. 显式转换层(任意策略组适用)

### 工作清单

- [ ] 设计统一接口族(建议):`explicit_cast`、`try_cast`、`checked_cast`
- [ ] 将边界、NaN、无穷大、精度与排序相关判定统一改为依赖 `algorithms::traits` / `algorithms::common_traits`
- [ ] 支持任意策略组组合,不绑定特定策略实现
- [ ] 风险可见化(截断/溢出/精度损失)并可程序化读取
- [ ] 定义失败语义(错误码或 expected 风格,按策略可配置)
Expand All @@ -51,28 +62,37 @@

- [ ] 所有跨类高风险转换必须走显式 API
- [ ] 风险信息可在编译期或运行期被确定性获取
- [ ] `conversion` 模块仅消费 `algorithms` 提供的元信息,不反向定义算法层协议

## M4. 算法层(以 max/min 为起点)
## M4. 算法层(traits 先行,以 max/min 为起点)

### 工作清单

- [ ] 确定模块结构:`mcpplibs.primitives.algorithms`、`mcpplibs.primitives.algorithms.traits`、`mcpplibs.primitives.algorithms.compare`、`mcpplibs.primitives.algorithms.minmax`
- [ ] 先实现 `algorithms::traits` 与 `algorithms::common_traits`,作为整个算法层与 conversion 的基础依赖
- [ ] 约束算法职责只涉及值的比较、选择、裁剪与边界处理,不提供目标类型导向的 cast API
- [ ] `max`/`min` 内部仅允许做 `common_rep` 归一化或排序语义协商,不绕回 conversion 层
- [ ] 实现 `max`/`min`,并预留 `clamp`/`compare` 扩展位
- [ ] 算法统一依赖 M2+M3 的公开接口,不绕过转换层
- [ ] 支持同类与受约束的异类输入
- [ ] 支持同类输入与受约束的异类输入;异类场景必须满足 `common_rep`、排序能力与策略组约束
- [ ] 明确 `partial_ordering` / `unordered` 语义,尤其是浮点 `NaN` 路径
- [ ] 为 `clamp`/`compare` 抽出复用内核:公共比较、边界判定、值选择
- [ ] 在可行范围保持 `constexpr`/`noexcept` 特性
- [ ] 增加边界测试(极值、NaN、有符号/无符号混合)
- [ ] 增加边界测试(极值、NaN、有符号/无符号混合、不同 `comparison_category`

### 验收标准

- [ ] 算法行为与策略约束一致
- [ ] 风险路径始终显式、可审计
- [ ] `algorithms` 可独立编译并被 `conversion` 依赖,不形成反向模块耦合
- [ ] 算法层实现不通过“显式转换 API”间接完成值比较

## 建议推进顺序

1. M2(先夯实约束与元信息基础)
2. M1(建立跨语言边界)
3. M3(收敛转换风险)
4. M4(复用基础能力实现算法)
2. M4 前半:`algorithms::traits` / `common_traits` / 排序能力查询
3. M3(让 conversion 改为依赖 algorithms 元信息)
4. M4 后半:`max` / `min` / `clamp` / `compare`
5. M1(建立跨语言边界,可按需求并行推进)

## 总体完成跟踪

Expand Down
6 changes: 6 additions & 0 deletions src/algorithms/algorithms.cppm
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
module;

export module mcpplibs.primitives.algorithms;

export import mcpplibs.primitives.algorithms.hash;
export import mcpplibs.primitives.algorithms.limits;
81 changes: 81 additions & 0 deletions src/algorithms/hash.cppm
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
module;

#include <concepts>
#include <cstddef>
#include <functional>
#include <type_traits>

export module mcpplibs.primitives.algorithms.hash;

import mcpplibs.primitives.underlying;

namespace mcpplibs::primitives::algorithms::details {

template <typename T>
concept std_hashable = requires(std::remove_cv_t<T> const &value) {
{
std::hash<std::remove_cv_t<T>>{}(value)
} -> std::convertible_to<std::size_t>;
};

} // namespace mcpplibs::primitives::algorithms::details

export namespace mcpplibs::primitives::algorithms {

template <typename T> struct hash {
using value_type = std::remove_cv_t<T>;
using result_type = std::size_t;

static constexpr bool enabled = false;

auto operator()(value_type const &) const noexcept -> result_type {
return result_type{};
}
};

template <typename T>
using hash_result_t = hash<std::remove_cvref_t<T>>::result_type;

template <typename T>
concept hashable = hash<std::remove_cvref_t<T>>::enabled;

template <typename T>
requires details::std_hashable<T>
struct hash<T> {
using value_type = std::remove_cv_t<T>;
using result_type = std::size_t;

static constexpr bool enabled = true;

auto operator()(value_type const &value) const noexcept(
noexcept(std::hash<value_type>{}(value))) -> result_type {
return std::hash<value_type>{}(value);
}
};

template <underlying_type T>
requires (!details::std_hashable<T> &&
!std::same_as<std::remove_cv_t<T>,
typename underlying::traits<std::remove_cv_t<T>>::rep_type> &&
hash<typename underlying::traits<std::remove_cv_t<T>>::rep_type>::enabled)
struct hash<T> {
using value_type = std::remove_cv_t<T>;
using rep_type = underlying::traits<value_type>::rep_type;
using result_type = hash_result_t<rep_type>;

static constexpr bool enabled = true;

auto operator()(value_type const &value) const noexcept(
noexcept(hash<rep_type>{}(underlying::traits<value_type>::to_rep(value))))
-> result_type {
return hash<rep_type>{}(underlying::traits<value_type>::to_rep(value));
}
};

template <hashable T>
auto hash_value(T const &value) noexcept(
noexcept(hash<std::remove_cvref_t<T>>{}(value))) -> hash_result_t<T> {
return hash<std::remove_cvref_t<T>>{}(value);
}

} // namespace mcpplibs::primitives::algorithms
199 changes: 199 additions & 0 deletions src/algorithms/limits.cppm
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
module;

#include <concepts>
#include <limits>
#include <type_traits>

export module mcpplibs.primitives.algorithms.limits;

import mcpplibs.primitives.underlying;

namespace mcpplibs::primitives::algorithms::details {

template <typename T>
concept has_numeric_limits =
std::numeric_limits<std::remove_cv_t<T>>::is_specialized;

template <typename T>
consteval auto category_from_type() -> underlying::category {
using value_type = std::remove_cv_t<T>;
if constexpr (std_bool<value_type>) {
return underlying::category::boolean;
} else if constexpr (std_char<value_type>) {
return underlying::category::character;
} else if constexpr (std::numeric_limits<value_type>::is_integer) {
return underlying::category::integer;
} else {
return underlying::category::floating;
}
}

} // namespace mcpplibs::primitives::algorithms::details

export namespace mcpplibs::primitives::algorithms {

template <typename T> struct limits {
using value_type = std::remove_cv_t<T>;
using rep_type = value_type;

static constexpr bool enabled = false;
static constexpr bool is_specialized = false;
static constexpr bool is_bounded = false;
static constexpr bool is_exact = false;
static constexpr bool is_signed = false;
static constexpr bool is_integer = false;
static constexpr bool is_iec559 = false;
static constexpr bool has_infinity = false;
static constexpr bool has_quiet_nan = false;
static constexpr int digits = 0;
static constexpr int digits10 = 0;
static constexpr int radix = 0;
static constexpr auto kind = static_cast<underlying::category>(-1);

static constexpr auto min() noexcept -> value_type { return {}; }
static constexpr auto lowest() noexcept -> value_type { return {}; }
static constexpr auto max() noexcept -> value_type { return {}; }
static constexpr auto epsilon() noexcept -> value_type { return {}; }
static constexpr auto infinity() noexcept -> value_type { return {}; }
static constexpr auto quiet_nan() noexcept -> value_type { return {}; }
};

template <typename T>
requires details::has_numeric_limits<T>
struct limits<T> {
using value_type = std::remove_cv_t<T>;
using rep_type = value_type;

static constexpr bool enabled = true;
static constexpr bool is_specialized = true;
static constexpr bool is_bounded = std::numeric_limits<value_type>::is_bounded;
static constexpr bool is_exact = std::numeric_limits<value_type>::is_exact;
static constexpr bool is_signed = std::numeric_limits<value_type>::is_signed;
static constexpr bool is_integer = std::numeric_limits<value_type>::is_integer;
static constexpr bool is_iec559 = std::numeric_limits<value_type>::is_iec559;
static constexpr bool has_infinity =
std::numeric_limits<value_type>::has_infinity;
static constexpr bool has_quiet_nan =
std::numeric_limits<value_type>::has_quiet_NaN;
static constexpr int digits = std::numeric_limits<value_type>::digits;
static constexpr int digits10 = std::numeric_limits<value_type>::digits10;
static constexpr int radix = std::numeric_limits<value_type>::radix;
static constexpr auto kind = details::category_from_type<value_type>();

static constexpr auto min() noexcept -> value_type {
return std::numeric_limits<value_type>::min();
}

static constexpr auto lowest() noexcept -> value_type {
return std::numeric_limits<value_type>::lowest();
}

static constexpr auto max() noexcept -> value_type {
return std::numeric_limits<value_type>::max();
}

static constexpr auto epsilon() noexcept -> value_type {
return std::numeric_limits<value_type>::epsilon();
}

static constexpr auto infinity() noexcept -> value_type {
return std::numeric_limits<value_type>::infinity();
}

static constexpr auto quiet_nan() noexcept -> value_type {
return std::numeric_limits<value_type>::quiet_NaN();
}
};

template <underlying_type T>
requires (!details::has_numeric_limits<T> &&
!std::same_as<std::remove_cv_t<T>,
typename underlying::traits<std::remove_cv_t<T>>::rep_type> &&
limits<typename underlying::traits<std::remove_cv_t<T>>::rep_type>::enabled)
struct limits<T> {
using value_type = std::remove_cv_t<T>;
using rep_type = underlying::traits<value_type>::rep_type;
using rep_limits = limits<rep_type>;

static constexpr bool enabled = true;
static constexpr bool is_specialized = rep_limits::is_specialized;
static constexpr bool is_bounded = rep_limits::is_bounded;
static constexpr bool is_exact = rep_limits::is_exact;
static constexpr bool is_signed = rep_limits::is_signed;
static constexpr bool is_integer = rep_limits::is_integer;
static constexpr bool is_iec559 = rep_limits::is_iec559;
static constexpr bool has_infinity = rep_limits::has_infinity;
static constexpr bool has_quiet_nan = rep_limits::has_quiet_nan;
static constexpr int digits = rep_limits::digits;
static constexpr int digits10 = rep_limits::digits10;
static constexpr int radix = rep_limits::radix;
static constexpr auto kind = underlying::traits<value_type>::kind;

static constexpr auto min() noexcept -> value_type {
return underlying::traits<value_type>::from_rep(rep_limits::min());
}

static constexpr auto lowest() noexcept -> value_type {
return underlying::traits<value_type>::from_rep(rep_limits::lowest());
}

static constexpr auto max() noexcept -> value_type {
return underlying::traits<value_type>::from_rep(rep_limits::max());
}

static constexpr auto epsilon() noexcept -> value_type {
return underlying::traits<value_type>::from_rep(rep_limits::epsilon());
}

static constexpr auto infinity() noexcept -> value_type {
return underlying::traits<value_type>::from_rep(rep_limits::infinity());
}

static constexpr auto quiet_nan() noexcept -> value_type {
return underlying::traits<value_type>::from_rep(rep_limits::quiet_nan());
}
};

template <typename T>
concept limited_type = limits<std::remove_cvref_t<T>>::enabled;

template <typename T>
using limit_value_t = limits<std::remove_cvref_t<T>>::value_type;

template <limited_type T>
constexpr auto min_value() noexcept(
noexcept(limits<std::remove_cvref_t<T>>::min())) -> limit_value_t<T> {
return limits<std::remove_cvref_t<T>>::min();
}

template <limited_type T>
constexpr auto lowest_value() noexcept(
noexcept(limits<std::remove_cvref_t<T>>::lowest())) -> limit_value_t<T> {
return limits<std::remove_cvref_t<T>>::lowest();
}

template <limited_type T>
constexpr auto max_value() noexcept(
noexcept(limits<std::remove_cvref_t<T>>::max())) -> limit_value_t<T> {
return limits<std::remove_cvref_t<T>>::max();
}

template <limited_type T>
constexpr auto epsilon_value() noexcept(
noexcept(limits<std::remove_cvref_t<T>>::epsilon())) -> limit_value_t<T> {
return limits<std::remove_cvref_t<T>>::epsilon();
}

template <limited_type T>
constexpr auto infinity_value() noexcept(
noexcept(limits<std::remove_cvref_t<T>>::infinity())) -> limit_value_t<T> {
return limits<std::remove_cvref_t<T>>::infinity();
}

template <limited_type T>
constexpr auto quiet_nan_value() noexcept(
noexcept(limits<std::remove_cvref_t<T>>::quiet_nan())) -> limit_value_t<T> {
return limits<std::remove_cvref_t<T>>::quiet_nan();
}

} // namespace mcpplibs::primitives::algorithms
Loading
Loading