#pragma once #include "pch.h" #include <Objbase....

Creado el: 31 de agosto de 2025

Respondido usando GPT-5 Thinking por Chat01

Pregunta

#pragma once

#include "pch.h"

#include <Objbase.h>

static inline float xor_rand_grsh(uint32_t& __restrict state) noexcept
{
uint32_t s = state;
s ^= s << 13u;
s ^= s >> 17u;
s ^= s << 5u;
state = s;
return fmaf(s, 0x1.0p-31f, -1.0f);
}

static inline float xor_rand(uint32_t& __restrict state)
{
uint32_t s = state;
s ^= s << 13u;
s ^= s >> 17u;
s ^= s << 5u;
state = s;
return s * 0x1.0p-32f;
}

static float HillFunc(const float x, uint32_t seed) noexcept
{
int32_t i = 0;
__declspec(align(64u)) float angles[14u];
const float start_angle = 6.28318530717958647692f * x;

#pragma loop(ivdep)
while (i < 14) {
angles[i] = start_angle * (i + 1);
++i;
}

text
__declspec(align(64u)) float sin_vals[14u]; __declspec(align(64u)) float cos_vals[14u]; fabe13_sincos_scalar_simple(angles, sin_vals, cos_vals, 14u); float res = fmaf(xor_rand(seed), 2.0f, -1.1f); --i; while (i >= 0) { res += fmaf(fmaf(xor_rand(seed), 2.0f, -1.1f), sin_vals[i], fmaf(xor_rand(seed), 2.0f, -1.1f) * cos_vals[i]); --i; } return res;

}

static inline float ShekelFunc(float x, uint32_t& __restrict seed)
{
uint32_t i = 0u;
float res = 0.0f;

text
while (i < 10u) { const float r = xor_rand(seed); const float x_part = fmaf(-r, 10.0f, x); res -= 1.0f / fmaf(fmaf(xor_rand(seed), 20.0f, 5.0f), x_part * x_part, fmaf(xor_rand(seed), 0.2f, 1.0f)); ++i; } return res;

}

static float GrishaginFunc(const float x1, const float x2, uint32_t seed) noexcept
{
int32_t i = 0;
__declspec(align(64u)) float angles_i[8u];
__declspec(align(64u)) float angles_j[8u];

#pragma loop(ivdep)
while (i < 8) {
const float pi_mult = 3.14159265358979323846f * (i + 1);
angles_i[i] = pi_mult * x1;
angles_j[i] = pi_mult * x2;
++i;
}

text
__declspec(align(64u)) float sin_i[8u], cos_i[8u]; __declspec(align(64u)) float sin_j[8u], cos_j[8u]; fabe13_sincos_scalar_simple(angles_i, sin_i, cos_i, 8u); fabe13_sincos_scalar_simple(angles_j, sin_j, cos_j, 8u); --i; float part1 = 0.0f; float part2 = 0.0f; while (i >= 0) { uint32_t j = 0u; while (j < 8u) { const float sin_term = sin_i[i] * sin_i[i]; const float cos_term = cos_j[j] * cos_j[j]; part1 = fmaf(xor_rand_grsh(seed), sin_term, fmaf(xor_rand_grsh(seed), cos_term, part1)); part2 = fmaf(-xor_rand_grsh(seed), cos_term, fmaf(xor_rand_grsh(seed), sin_term, part2)); ++j; } --i; } return -sqrtf(fmaf(part1, part1, part2 * part2));

}

static inline float RastriginFunc(const float x1, const float x2) noexcept
{
const float term1 = fmaf(x1, x1, x2 * x2);
return (term1 - fmaf(
fabe13_cos(6.28318530717958647692f * x1) +
fabe13_cos(6.28318530717958647692f * x2),
10.0f,
-14.6f)) *
fmaf(-term1, 0.25f, 18.42f);
}

static inline float Shag(const float _m, const float x1, const float x2, const float y1,
const float y2, const uint32_t _N, const float _r) noexcept
{
const float diff = y2 - y1;
return _N == 1u
? fmaf(-(1.0f / _m), diff, x1 + x2) * 0.5f
: diff > 0.0f
? fmaf(-(1.0f / (_m * _m)), (diff) * (diff)_r, x1 + x2) * 0.5f
: fmaf(1.0f / (_m * _m), (diff) * (diff)
_r, x1 + x2) * 0.5f;
}

__declspec(align(32u)) struct Interval final
{
public:
std::pair<float, float> start;
std::pair<float, float> end;
float M;
float R;

text
__declspec(noalias) __forceinline Interval(const std::pair<float, float> _start, const std::pair<float, float> _end, const uint32_t _N) : start(_start) , end(_end) , M(_N == 1u ? fabsf(_end.second - _start.second) * (1.0f / (_end.first - _start.first)) : _N == 2u ? fabsf(_end.second - _start.second) * (1.0f / sqrtf(_end.first - _start.first)) : fabsf(_end.second - _start.second) * (1.0f / powf(_end.first - _start.first, 1.0f / float(_N)))) , R(0.0f) { } __declspec(noalias) __forceinline void ChangeCharacteristic(const float _m, const uint32_t _N) noexcept { const float delta_x = end.first - start.first; const float delta_y = end.second - start.second; switch (_N) { case 1u: R = fmaf( -(end.second + start.second), 2.0f, fmaf(_m, delta_x, (delta_y * delta_y) * (1.0f / (_m * delta_x)))); break; case 2u: { const float sqrt_dx = sqrtf(delta_x); R = fmaf( -(end.second + start.second), 2.0f, fmaf(_m, sqrt_dx, (delta_y * delta_y) * (1.0f / (_m * sqrt_dx)))); } break; default: { const float pow_dx = powf(delta_x, 1.0f / _N); R = fmaf( -(end.second + start.second), 2.0f, fmaf(_m, pow_dx, (delta_y * delta_y) * (1.0f / (_m * pow_dx)))); } } }

};

//static inline bool Compare(const Interval& __restrict a, const Interval& __restrict b) noexcept
//{
//return a.R < b.R;
//}

struct IntervalComparator final {
inline bool operator()(const Interval a, const Interval b) {
if (a.R < b.R) return true;
if (a.R == b.R && a.start.first > b.start.first) return true;
return false;
//return a.R < b.R;
}
};

typedef std::priority_queue<Interval, std::deque<Interval>, IntervalComparator>
Priority_queue;

const __declspec(noalias) __forceinline PeanoCurve_2D* PeanoCurve_2D::HitTest_2D(float x) const noexcept
{
uint32_t i = 0u;
const uint32_t _razvertka = this->razvertka;
uint32_t num;
const float this_a = this->a;
x -= this_a;
const float b_minus_a = this->b - this_a;
const float inv_b_minus_a = 1.0f / b_minus_a;
const PeanoCurve_2D* Curr = this;

text
while (i != _razvertka) { const uint32_t shift = 1u << ++i + i; num = shift * x * inv_b_minus_a; x = fmaf(-ldexp(1.0f, -(i << 1)) * num, b_minus_a, x); const List currType = Curr->Type; switch (num) { case 0u: Curr = (currType == Top || currType == Right) ? Curr->DownLeft.get() : Curr->TopRight.get(); break; case 1u: Curr = (currType == Top || currType == Left) ? Curr->TopLeft.get() : Curr->DownRight.get(); break; case 2u: Curr = (currType == Top || currType == Right) ? Curr->TopRight.get() : Curr->DownLeft.get(); break; case 3u: Curr = (currType == Top || currType == Left) ? Curr->DownRight.get() : Curr->TopLeft.get(); } } return Curr;

}

const __declspec(noalias) __forceinline float PeanoCurve_2D::FindX_2D(float target_x1, float target_x2) const noexcept
{
uint32_t _razvertka = this->razvertka;
uint32_t _razvertka1 = _razvertka;
float x1, x2, x = this->a;
const float b_minus_a = this->b - x;
const PeanoCurve_2D* Curr = this;

text
while (_razvertka != 0u) { const uint32_t exponent = _razvertka1 - _razvertka-- << 1u; x1 = Curr->x1; x2 = Curr->x2; const List currType = Curr->Type; if (target_x1 > x1 && target_x2 > x2) { Curr = Curr->TopRight.get(); if (currType == Top || currType == Right) { x = fmaf(ldexp(1.0f, -exponent) * 0.5f, b_minus_a, x); } } else if (target_x1 < x1 && target_x2 > x2) { Curr = Curr->TopLeft.get(); if (currType == Top || currType == Left) { x = fmaf(ldexp(1.0f, -exponent) * 0.25f, b_minus_a, x); } else { x = fmaf(ldexp(1.0f, -exponent) * 0.75f, b_minus_a, x); } } else if (target_x1 < x1 && target_x2 < x2) { Curr = Curr->DownLeft.get(); if (currType == Down || currType == Left) { x = fmaf(ldexp(1.0f, -exponent) * 0.5f, b_minus_a, x); } } else { Curr = Curr->DownRight.get(); if (currType == Top || currType == Left) { x = fmaf(ldexp(1.0f, -exponent) * 0.75f, b_minus_a, x); } else { x = fmaf(ldexp(1.0f, -exponent) * 0.25f, b_minus_a, x); } } } return x;

}

extern const boost::mpi::communicator world;

extern std::unique_ptr<PeanoCurve_2D> Curve;
extern std::unique_ptr<PeanoCurve_2D> Curve_Inverted;

struct CrossMsg final {
float s_x1, s_x2;
float e_x1, e_x2;
float Rtop;
template <typename Archive>
__declspec(noalias) __forceinline void serialize(Archive& __restrict ar, const unsigned int) noexcept { ar& s_x1& s_x2& e_x1& e_x2& Rtop; }
};

static std::vector<float> Base_LNA_1_2_Mer_AGP(const uint32_t N,
const uint32_t global_iterations, const float a, const float b, const float c, const float d, const float r,
const bool mode, const float epsilon, uint32_t seed)
{

text
if (N == 1u) { const float initial_length = b - a; float dmax = initial_length; std::vector<float> Extr; Extr.reserve((global_iterations << 1u) + 2u); //std::vector<std::unique_ptr<Interval>> R; //R.reserve(global_iterations + 1u); Priority_queue R; float best_f = ShekelFunc(a, seed); const float end_val = ShekelFunc(b, seed); uint32_t schetchick = 0u; std::pair<float, float> x_Rmax = std::make_pair(a, b); std::pair<float, float> y_Rmax = std::make_pair(best_f, end_val); Interval interval(std::pair(a, best_f), std::pair(b, end_val), N); R.push(interval); //R.emplace_back(std::make_pair(a, best_f), std::make_pair(b, end_val), 1u); //float Mmax = R.front().M; //float m = r * Mmax; std::vector<float> myVector = { 1.1f, 2.2f, 3.3f, 4.4f, 5.5f }; // 2. Делаем что-то с вектором (например, увеличиваем каждый элемент на 10%) for (float& num : myVector) { // Используем ссылку, чтобы изменять оригинальные значения num *= 1.1f; } // 3. Добавляем новый элемент в конец вектора myVector.push_back(6.6f); // 4. Возвращаем модифицированный вектор return myVector; }

}

// C-обёртка: без std::vector в сигнатуре!
extern "C" __declspec(dllexport) int __cdecl Base_LNA_1_2_Mer_AGP_C(
uint32_t N, uint32_t global_iterations, float a, float b, float c, float d, float r,
bool mode, float epsilon, uint32_t seed,
float** out_data, uint32_t* out_len)
{
try {
auto v = Base_LNA_1_2_Mer_AGP(N, global_iterations, a, b, c, d, r, mode, epsilon, seed);
*out_len = (uint32_t)v.size();
out_data = (float)CoTaskMemAlloc(sizeof(float) * (*out_len));
if (!*out_data) return 2; // OOM
memcpy(*out_data, v.data(), sizeof(float) * (*out_len));
return 0; // ok
}
catch (const std::bad_alloc& e) {
if (out_data) *out_data = nullptr;
if (out_len) *out_len = 0;
return 3;
}
catch (const std::exception& e) {
if (out_data) *out_data = nullptr;
if (out_len) *out_len = 0;
return 1;
}
}

extern "C" __declspec(dllexport) void __cdecl Base_LNA_1_2_Mer_AGP_Free(float* p) {
CoTaskMemFree(p);
} - у меня есть проблема с моей DLL библиотекой, проблема не в самих экспортируемых функциях - проблема в основной функции конструирующей вектор - проблема в том, что я могу сконструировать интервал но не могу положить его в вектор или дэк или приоритетную очередь - я перепробовал все эти структуры хранения и всегда получал исключения, если сделать через emplace_back то тоже получу исключение - причём заметь когда я делаю всё это с обычным вектором примитивного типа то вставка проходит нормально но когда я делаю вставку в вектор/дэк/очередь интервалов то получаю исключение, всё остальное было проверено исключений не вызывает - больше не нашёл ошибок, эта функция ещё в разработке, эта библиотека чисто нативная - однако вызывается она в управляемом коде, более того - мной было протестировано раньше в чисто нативном приложении что вставка интервалов работает нормально, но когда я вызываю эту функцию в MyForm.h clr NetFramework 4.8.1 так System::Void button1_Click(System::Object^ sender, System::EventArgs^ e) {

text
chart2->Series[0]->Points->Clear(); chart2->Series[1]->Points->Clear(); chart2->Series[2]->Points->Clear(); chart2->Series[3]->Points->Clear(); static LARGE_INTEGER start, end; QueryPerformanceCounter(&start); uint32_t seed = GetTickCount(); typedef int(__cdecl* agp_c)( uint32_t, uint32_t, float, float, float, float, float, bool, float, uint32_t, float**, uint32_t*); typedef void(__cdecl* agp_free)(float*); HINSTANCE h = LoadLibrary(L"TEST_FUNC.dll"); if (!h) { DWORD errorCode = GetLastError(); System::String^ errorMessage = "Ошибка загрузки DLL! Код: " + errorCode.ToString(); System::Windows::Forms::MessageBox::Show(errorMessage); return; } auto f = (agp_c)GetProcAddress(h, "Base_LNA_1_2_Mer_AGP_C"); auto ffree = (agp_free)GetProcAddress(h, "Base_LNA_1_2_Mer_AGP_Free"); if (!f || !ffree) { /* обработать ошибку */ } float* buf = nullptr; uint32_t len = 0; int rc = f(1, 10000, 0.0f, 1.0f, -2.2f, 1.8f, 2.5f, false, 0.0001f, seed, &buf, &len); if (rc == 0) { std::vector<float> Extr_2D(buf, buf + len); ffree(buf); textBox2->Text = Convert::ToString(Extr_2D.back()); // ... используете Extr_2D } FreeLibrary(h); то получаю исключение - внешний компанент вызывал исключение SEH глубоко проанализируй в чём тут дело, дело ли в управляемом коде, я не могу отказаться от управляемого кода так как мне нужен вывод на графики, предложи возможные пути решения проблемы

Pensamiento

Analyzing the C++/CLI issue

It looks like the problem arises when inserting an Interval object into a container from managed code, triggering exceptions. This could stem from alignment issues with std::pair<float, float> inside Interval. Specifically, custom alignment with __declspec(align(32)) might be handled differently in MSVC, affecting memory allocation in containers like std::deque.

Considering alignment and SEH

It seems like the issue might relate to alignment requirements for over-aligned types, especially since C++17 introduces a specific aligned new operator. MSVC with /Zc:alignedNew deals with up-to-16-byte alignment, and perhaps the DLL might need more alignment handling. The SEH exception occurs during container insertion, possibly due to stack-aligned arrays or other assumptions in the Interval struct's memory. The clarification around /Zc:stackalign might help resolve this.

Considering stack alignment issues

In x86 (32-bit), stack alignment can vary between 4 or 16 bytes, and SSE instructions need 16-byte alignment. When combining /clr (managed) code and native code, stack misalignment can cause crashes. The issue might arise with constructing Interval in the native DLL, especially if SSE/AVX instructions are involved, requiring 32-byte alignment.

Considering alignment and allocation

The struct has a 32-byte alignment specification, and inserting it into a container may allocate memory with this alignment. However, earlier versions of MSVC (pre-2017) didn’t handle extended alignment correctly unless /Zc:alignedNew was specified. Since the issue occurs only in the managed environment, it might be related to floating-point exceptions or CRT differences.

Analyzing container memory and alignment issues

The issue could stem from passing the over-aligned Interval type by value in the comparator, as this requires 32-byte alignment. Under /clr, the stack alignment might be insufficient, leading to misalignment when the comparator is invoked. Changing the comparator to take arguments by reference should resolve this.

Addressing alignment and memory issues

The key issue here seems related to passing over-aligned types by value in a comparator across the /clr boundary, which could cause stack misalignment. To resolve this:

  1. Change the comparator to use references instead of values.
  2. Adjust __declspec(align) on Interval or use alignas(16).
  3. Ensure the stack alignment is correct for function calls, or pass the alignment check earlier in the call hierarchy.

Thought for 1m 47s

Respuesta

Коротко: у тебя на стыке native/managed выстрелила ошибка выравнивания (alignment) + неудачная сигнатура компаратора. Всё отлично работает в чисто нативном приложении, а под WinForms (/clr) ловишь SEH при вставке Interval в vector/deque/priority_queue, потому что:

  • Interval помечен __declspec(align(32)) ⇒ тип over-aligned (требует 32-байтового выравнивания).
  • Твой IntervalComparator принимает параметры по значению, т.е. компилятор кладёт копии Interval на стек.
  • Под /clr (особенно x86 / “Prefer 32-bit”) стек вызываемой функции может быть выровнен не на 32, а иногда даже не на 16. Компилятор при этом верит, что параметры выровнены как у типа, и генерирует aligned load/store → доступ по невыравненному адресуSEH “внешний компонент вызвал исключение”.
  • В «нативном» тесте это не проявляется, потому что стек к моменту вызова реально был достаточно выровнен (или компилятор сгенерировал безопасные инструкции).

Добавочно усугубляют ситуацию твои локальные массивы с __declspec(align(64)) в других функциях — под /clr(x86) это особенно опасно: стек там часто всего 4/8-байтово выровнен.


Что сделать (по важности)

1) Исправить компаратор — принимать по const&

Это главная правка: не копируй over-aligned объекты на стек.

cpp
struct IntervalComparator final { bool operator()(const Interval& a, const Interval& b) const noexcept { if (a.R < b.R) return true; if (a.R == b.R && a.start.first > b.start.first) return true; return false; } };

(Сейчас у тебя operator()(Interval a, Interval b), что и провоцирует копирование/размещение на стеке.)

2) Убери избыточное выравнивание у Interval (или хотя бы снизь)

Если 32 байта тебе не жизненно необходимы — удали __declspec(align(32)) или замени на alignas(16).
Чем меньше требуемое выравнивание, тем меньше шансов словить падение на стеке/в контейнерах.

cpp
// было: __declspec(align(32u)) struct Interval // стало, если всё же нужно: alignas(16) struct Interval struct Interval final { ... };

Примечание: если ультра-выравнивание реально нужно (SIMD и т.п.), никогда не передавай такие типы по значению, и избегай их размещения на стеке. Держи их по указателю/ссылке.

3) Собери и приложение, и DLL под x64 (и отключи “Prefer 32-bit”)

На x64 по ABI стек выровнен на 16 байт, и жизнь сильно проще.
В проекте WinForms (.NET Framework 4.8.1):

  • Platform target: x64
  • Сними галку Prefer 32-bit
    В нативном проекте — тоже x64.

4) Если сохраняешь over-aligned типы в контейнерах STL

  • Убедись, что сборка ведётся с /Zc:alignedNew (в MSVC это важно для типов с выравниванием > max_align_t).
  • Используй ссылки/указатели в компараторах, лямбдах, std::function, и т.п.
  • По возможности избегай хранения сильно выровненных «тяжёлых» объектов по значению — храни std::unique_ptr<Interval> (или лёгкие ключи/индексы), а сравнивай по ссылке на целевой объект.

5) Локальные массивы с __declspec(align(64))

В функциях вроде HillFunc, GrishaginFunc у тебя есть стековые массивы с 64-байтовым выравниванием. Под /clr(x86) это почти гарантированная мина. Варианты:

  • Снизить до alignas(16).
  • Или выделять на куче через _aligned_malloc(64, size)/_aligned_free (или завести свой AlignedAllocator и использовать std::vector<T, AlignedAllocator<T, 64>>).
  • На x64 проблема менее вероятна, но лучше всё равно не полагаться на сверх-выравнивание стека.

6) Защита от SEH, чтобы возвращать код ошибки вместо падения

Сейчас ты ловишь только std::exception/bad_alloc. Доступ по неверному адресу — это SEH, он сюда не попадает. Если хочется «мягко» отловить и вернуть код:

  • Скомпилируй DLL с /EHa и добавь catch(...) внутри экспортируемой C-обёртки вокруг вызова нативной функции, или
  • Используй __try { ... } __except(EXCEPTION_EXECUTE_HANDLER) { ... }.

Это не лечит причину, но даст аккуратный rc != 0 без выноса процесса.

7) Мелкие замечания по коду

  • В Priority_queue ты используешь std::priority_queue<Interval, std::deque<Interval>, IntervalComparator>. С переходом на const& в компараторе — ок.
    Если всё равно некомфортно — можешь временно заменить на std::vector<Interval> + std::push_heap/pop_heap (там меньше «магии») — чисто для диагностики.
  • В HitTest_2D есть const uint32_t shift = 1u << ++i + i; — приоритет операторов: это 1u << (++i + i). Убедись, что именно так задумывалось.
  • В FindX_2D много ветвлений с ldexp(1.0f, -exponent) * K — всё ок, но проверь, что exponent не уезжает за 24 (точность float).

Короткий план правок (минимум для устранения падения)

  1. Компаратор:
cpp
struct IntervalComparator final { bool operator()(const Interval& a, const Interval& b) const noexcept { if (a.R < b.R) return true; if (a.R == b.R && a.start.first > b.start.first) return true; return false; } };
  1. Сборка x64, отключить “Prefer 32-bit”.

  2. Снизить выравнивание Interval до alignas(16) или убрать вовсе. (И не передавать по значению нигде.)

  3. (Опционально) В C-обёртке добавить catch(...) или __try/__except, чтобы вместо выноса процесса возвращался код ошибки.


Если по каким-то причинам нужно оставить 32/64-байтовое выравнивание

  • Храни Interval по указателю:
    cpp
    using PInterval = std::unique_ptr<Interval, AlignedDeleter>; // _aligned_free using PQ = std::priority_queue<PInterval, std::vector<PInterval>, PtrComparator>;
  • Свой аллокатор для контейнера:
    cpp
    template<class T, std::size_t Align> struct AlignedAllocator { /* реализуй allocate/deallocate через _aligned_malloc/_aligned_free */ }; std::vector<Interval, AlignedAllocator<Interval, 32>> v;
  • В компараторах — всегда const Interval& (или сравнение по *ptr).
  • Не создавай такие объекты на стеке. Только в куче.

Почему это выглядит именно как «из-за управляемого кода»

  • В чисто нативном тесте компилятор/рантайм часто обеспечивает «удачное» выравнивание, и копирование по значению не падает.
  • Под WinForms (/clr), особенно в x86, входной стек при переходе из managed в native может быть выровнен «хуже», а компилятор твоей DLL уверенно применяет aligned инструкции, потому что видит align(32) у параметров по значению → конфликт ожиданий и реальности → SEH.

Если применишь первые три пункта (компаратор по ссылке, x64, уменьшить выравнивание/не класть на стек) — проблема должна исчезнуть. Если нет — пингани, накину набор assert’ов/trace и подскажу, где ещё «торчит» копирование по значению или злоупотребление выравниванием.

Compartir este Q&A