#pragma pack(push,1) // this pragma set alignment of elements in structure to 1 bytestruct RateInfo
{
unsigned int ctm;
double open;
double low;
double high;
double close;
double vol;
};
#pragma pack(pop) // this pragma set alignment by default
Как бы мне эти алигменты указать в C#? Массив таких структур записан в файле, я хочу использовать
FileMapping и читать, как массив структур, наверное, без указания алигмента не обойтись?
Здравствуйте, vog, Вы писали:
vog>Здравствуйте!
vog>Есть С++ структура
vog>Как бы мне эти алигменты указать в C#? Массив таких структур записан в файле, я хочу использовать vog>FileMapping и читать, как массив структур, наверное, без указания алигмента не обойтись? [StructLayout(LayoutKind.Sequential)]
Здравствуйте, vog, Вы писали:
vog>Как бы мне эти алигменты указать в C#? Массив таких структур записан в файле, я хочу использовать vog>FileMapping и читать, как массив структур, наверное, без указания алигмента не обойтись?
А смысл делать FileMapping, если тебе для прочтения любого поля будет маршаллиться т.е. долго и нудно копироваться вся структура, да еще и через рефлексию? Автоматический маршаллинг убъет нафик любой выигрыш от FileMapping.
Сделай обертку типа такого:
unsafe struct RateInfo
{
private byte* _addr;
public RateInfo(IntPtr addr) {
_addr = addr.ToPointer();
}
uint Ctm { get { return *(uint*)_addr; } }
double Open { get { return *(double*)(_addr+4); } }
double Low { get { return *(double*)(_addr+12); } }
...
}
Структура хранит адрес элемента, с которым работаешь, а в св-вах ручками пишем код, который нам нарисовал бы какой-нить C++/CLI-компилятор при обращении к полям твоей исходной структуры, т.е. будет работать с нативной скоростью без проседания быстродействия в сотню раз на маршаллинге.
Идеально было бы сделать это всё не ручками, а создать вспомогательную либу именно в упомянутом C++/CLI, дабы сохранить принцип реализации, но убрать нелицеприятный хардкод.
Принцип там будет примерно такой:
#pragma unmanaged
#pragma (push, пиф, паф и что угодно из С++)
struct NativeRateInfo {
unsigned ctm;
...
};
#pragma managed
public value class RateInfo {
NativeRateInfo* ptr;
public:
RateInfo(NativeRateInfo* p) : ptr(p) {}
// :)void Next() { ptr++; }
unsigned Ctm { unsigned get() { return ptr.ctm; } }
...
};
Здравствуйте, vdimas, Вы писали:
V>А смысл делать FileMapping, если тебе для прочтения любого поля будет маршаллиться т.е. долго и нудно копироваться вся структура, да еще и через рефлексию? Автоматический маршаллинг убъет нафик любой выигрыш от FileMapping.
Придумал такой вариант без маршаллинга, но в unsafe:
[StructLayout(LayoutKind.Sequential, Pack = 1)] // Pack=1 тоже жизненно необходимо, по умолчанию выравнивается вроде по 4 байта или определяется платформойunsafe struct RateInfo
{
uint ctm;
double open;
double low;
double high;
double close;
double vol;
public fixed char copyright[64]; // ну или как там у вас...
}
//... а в соответствующей процедуре так:byte[] buf = new byte[sizeof(RateInfo)];
GCHandle handle = GCHandle.Alloc(buf, GCHandleType.Pinned);
FileStream fs = new FileStream("infile");
fs.Read(buf, 0, buf.Length); // ну или как то иначе заполняем
RateInfo* ptr = (RateInfo*)handle.AddrOfPinnedObject().ToPointer();
//дальше поля структуры доступны через операцию ->
// а когда все закрывается и уничтожается не забыть освободить указатель
handle.Free();
Ну естественно, все это лучше обернуть в аккуратно написанный класс И никакого маршаллинга и лишнего копирования, а тем более процедур отражения.
Здравствуйте, Spirit_1, Вы писали:
S_>//... а в соответствующей процедуре так: S_> byte[] buf = new byte[sizeof(RateInfo)]; S_> GCHandle handle = GCHandle.Alloc(buf, GCHandleType.Pinned); S_> FileStream fs = new FileStream("infile"); S_> fs.Read(buf, 0, buf.Length); // ну или как то иначе заполняем S_> RateInfo* ptr = (RateInfo*)handle.AddrOfPinnedObject().ToPointer(); S_>//дальше поля структуры доступны через операцию ->
Непонятен только смысл приседаний с GCHandle — намного проще сразу получить fixed указатель на первый элемент и уже потом закастить его к RateInfo*
Если у Вас нет паранойи, то это еще не значит, что они за Вами не следят.
Здравствуйте, TK, Вы писали:
TK>Непонятен только смысл приседаний с GCHandle — намного проще сразу получить fixed указатель на первый элемент и уже потом закастить его к RateInfo*
Ну как бы двух зайцев убиваем. Во-первых, первый элемент структуры может поменяться в процессе кодинга и надо будет вспоминать, где ты там делал этот fixed *, или вообще быть неизвестным, если писать шаблон. Ну и конечно как и с fixed защита от перемещения GC, только с большим временем, если переменные нелокальные. Тогда логично операции выделения буфера и приведения указателя поместить в конструктор, сделать буфер и указатель приватными полями и забыть про GC до самого Dispose.
Здравствуйте, Spirit_1, Вы писали:
S_>Здравствуйте, TK, Вы писали:
TK>>Непонятен только смысл приседаний с GCHandle — намного проще сразу получить fixed указатель на первый элемент и уже потом закастить его к RateInfo*
S_>Ну как бы двух зайцев убиваем. Во-первых, первый элемент структуры может поменяться в процессе кодинга и надо будет вспоминать, где ты там делал этот fixed *, или вообще быть неизвестным, если писать шаблон.
С шаблоном этот номер не пройдет!
Здравствуйте, samius, Вы писали:
S_>>Ну как бы двух зайцев убиваем. Во-первых, первый элемент структуры может поменяться в процессе кодинга и надо будет вспоминать, где ты там делал этот fixed *, или вообще быть неизвестным, если писать шаблон. S>С шаблоном этот номер не пройдет!
Да? Извините, видимо не шарю. А жаль, хотел написать. А то сильно скучаю по сишному fread
Здравствуйте, Spirit_1, Вы писали:
S_>Ну как бы двух зайцев убиваем. Во-первых, первый элемент структуры может поменяться в процессе кодинга и надо будет вспоминать, где ты там делал этот fixed *, или вообще быть неизвестным, если писать шаблон.
Я пока вижу только то, что в случае исключения GCHandle.Free вызван не будет. С AddrOfPinnedObject проблема аналогичная — если структура поменялась то, придется вспоминать где она могла использоваться.
S_>Ну и конечно как и с fixed защита от перемещения GC, только с большим временем, если переменные нелокальные.
Что есть "нелокальные переменные"
Если у Вас нет паранойи, то это еще не значит, что они за Вами не следят.
Здравствуйте, Spirit_1, Вы писали:
S_>Здравствуйте, samius, Вы писали:
S_>>>Ну как бы двух зайцев убиваем. Во-первых, первый элемент структуры может поменяться в процессе кодинга и надо будет вспоминать, где ты там делал этот fixed *, или вообще быть неизвестным, если писать шаблон. S>>С шаблоном этот номер не пройдет!
S_>Да? Извините, видимо не шарю. А жаль, хотел написать. А то сильно скучаю по сишному fread
Не стоит извиняться... Сам регулярно делаю попытки написать такой шаблон и каждый раз забываю, чем это кончается. Последний раз был не так давно, потому память о неудаче еще свежа
Вообще, если бы было специальное ограничение на тип шаблона:
where T: unmanaged
То можно было бы и шаблонный метод сделать.
Но такое требуется довольно редко, большой необходимости в этом нет.
Здравствуйте, TK, Вы писали:
TK>Я пока вижу только то, что в случае исключения GCHandle.Free вызван не будет. С AddrOfPinnedObject проблема аналогичная — если структура поменялась то, придется вспоминать где она могла использоваться.
Насчет исключений согласен. Если это критично, такой вариант не пойдет. А если например создание буфера и приведение к нему указателя находятся в одном методе, а обращения к полям через указатель в другом, то о каком fixed может быть речь?
TK>Что есть "нелокальные переменные"
Я имел в виду примерно такую реализацию "обертки"
unsafe struct RateInfo // наша структура
{
public double open;
//...
}
unsafe class StructReader
{
byte[] buf; // вот это я назвал "нелокальными переменными"
RateInfo* ptr;
GCHandle handle;
FileStream fs;
public StructReader()
{
byte[] buf = new byte[sizeof(RateInfo)];
handle = GCHandle.Alloc(buf, GCHandleType.Pinned);
//ptr = (RateInfo*)buf; // так пишет Cannot convert ...
ptr = (RateInfo*)handle.AddrOfPinnedObject().ToPointer();
}
public void OpenDataFile(string filename)
{
fs = new FileStream(filename,FileMode.Open);
}
public void ReadNext()
{
fs.Read(buf, 0, buf.Length);
}
// Обертка структурыpublic double open
{
get { return ptr->open; }
set { ptr->open = value; }
}
// etc...public void CloseDataFile()
{
fs.Close();
handle.Free();
}
}
Конечно, можно в каждом аксессоре ставить fixed и приводить указатели, но как то это не очень...
Здравствуйте, samius, Вы писали:
S>Вообще, если бы было специальное ограничение на тип шаблона: S>
S>where T: unmanaged
S>
S>То можно было бы и шаблонный метод сделать.
Это, наверное, противоречит принципу работы шаблонов в C#.
S>Но такое требуется довольно редко, большой необходимости в этом нет.
Да вот к сожалению не все файлы пока записываются в формате XML или сериализацией объектов. Просто имею дело с форматами записи сейсмических данных, которые увы не мной придуманы и которых надо придерживаться как общепринятых отраслевых стандартов. В C++ их чтение никаких затруднений не вызывает, а в C# эта казалось бы простая операция превратилась в танцы с бубном.
Здравствуйте, Spirit_1, Вы писали:
S_>Здравствуйте, samius, Вы писали:
S>>Вообще, если бы было специальное ограничение на тип шаблона: S>>
S>>where T: unmanaged
S>>
S>>То можно было бы и шаблонный метод сделать. S_>Это, наверное, противоречит принципу работы шаблонов в C#.
Если бы компилятор умел отслеживать, что в структуре нет managed полей, то это ничему бы не противоречило.
S>>Но такое требуется довольно редко, большой необходимости в этом нет. S_>Да вот к сожалению не все файлы пока записываются в формате XML или сериализацией объектов. Просто имею дело с форматами записи сейсмических данных, которые увы не мной придуманы и которых надо придерживаться как общепринятых отраслевых стандартов. В C++ их чтение никаких затруднений не вызывает, а в C# эта казалось бы простая операция превратилась в танцы с бубном.
Если таких структур больше, чем хотелось бы писать методов конвертации для каждой, то можно воспользоваться Emit-ом и нагенерить сериализаторов, прикрутить их к generic классу, в конце концов.