Я пытаюсь упростить кодовую базу C / C ++, которая вызывает SQL операторы БД сервера и / или хранимые процедуры из SQL сервера путем написания шаблонов variadi c различного рода. Я успешно написал функцию doQuery, которая принимает дополнительные параметры, которые автоматически связываются с запросом по типу, например, логическое, двойное, строковое, в качестве аргументов ввода или ввода / вывода.
Текущая проблема:
Для запросов, которые возвращают наборы результатов, я изо всех сил пытаюсь произвести оптимальные простые вызовы шаблонов.
Я пробовал кортежи, но они непопулярны среди традиционной команды разработчиков C, и мне не нравится они сами.
Так что я принимаю функцию, которая вызывается обратно с данными каждой строки, которую пользователь может выбрать для вставки / init {} в любой форме, например, pu sh в захваченный вектор структур , Исходя из сигнатуры обратного вызова, я генерирую привязки SQLBindCol для ODB C степени сложности n в массиве информации привязки для возвращаемых столбцов.
например,
std::vector<MyStruct> result;
auto functor = [&result](int id, string name, bool isOK, string address) {
result.push_back(MyStruct{id,name,isOK,address});
}
c.doQuery(functor, "select id, name, isOK, address from MyTable;" /**/);
( /**/
показывает, где можно указать параметры для заполнения '?', Что у меня прекрасно работает, но в этом простом примере его нет).
Проблема возникает при попытке вызвать функцию, повторно связывающую результаты строки запроса:
template <typename T...>
InternalFunction(T)
[...]
vector<Binding> columns;
[...]
int i = 0;
functor(GetResult<T>(columns, i++)...);
GetResult возвращает столбец из i-го элемента вектора привязки в виде нужный тип в вызове функции. Но C ++ не гарантирует порядок оценки аргументов. Итак, согласно { ссылка } в потоке Как гарантировать порядок вычисления аргументов при вызове объекта функции? , решение довольно простое:
struct OrderedCall
{
template <typename F, typename ...Args>
OrderedCall(F && f, Args &&... args)
{
std::forward<F>(f)(std::forward<Args>(args)...);
}
};
[...]
OrderedCall{functor, GetResult<T>(columns, i++)...};
К сожалению, Visual Studio 15 (MSV C) явно не работает и все еще переупорядочивает оценки, например, если есть строковый аргумент, который вычисляется последним, что приводит к результатам происходят из произвольно выбранных членов вектора-столбца, а не выбирают их по порядку, так как они были автоматически связаны с использованием методов variadi c.
Я не знаю другого способа связать аргументы с последовательными векторные записи для решения этой проблемы и обеспечения надежной работы обратного вызова функтора.
Если я отключу оптимизацию и встраивание функций и уберу трюк со структурой OrderedCall, оценки будут происходить довольно надежно в обратном порядке, который можно использовать, но это не так. хорошее решение, потому что оно ни практично, ни надежно.
Я не могу намек на другой способ вызова функции во время: повторного связывания аргументов со столбцами, сохранения типов аргументов и т. д. c.
Весь смысл в том, чтобы сделать привязку простой и естественной, поэтому кортежи исключены вместе с другими решениями, которые портят модель вызова.
Так что мои вопросы:
Есть ли способ вызвать мою функцию std :: function с помощью set аргументов, генерируемых шаблонной функцией из вектора? Я посмотрел на std :: bind, но, похоже, он не предлагал простой способ решения проблемы, но, возможно, итеративное связывание одного аргумента за раз, вывод одной функции из другой из другой ...
Должен ли я создать рекурсивный шаблон только для вызова функтора? (это была бы уродливая реализация, в лучшем случае передававшая аргументы через многоуровневую сложность, если она вообще работала).
Я пропустил более простое / лучшее решение для упрощения его до pu sh привести строки в вектор структур?
Я заново изобретаю колесо? Это упрощение связывания variadi c уже доступно для sql -server / odb c / msv c?
Не нарушена ли более поздняя версия Visual Studio, как эта ?
Я сам ответил на поломку VS следующим тестом:
#include <iostream>
#include <string>
struct OrderedCall
{
template <typename F, typename ...Args>
OrderedCall(F && f, Args &&... args)
{
std::forward<F>(f)(std::forward<Args>(args)...);
}
};
void foo(int, std::string s, char, bool) {
int i = 0;
i++;
}
int bar() {
return 10;
}
template <typename T>
T echo(T x) {
std::cout << " " << x;
return x;
}
int main(int argc, char**argv) {
std::string goo = "Goo";
std::cout << "Occurance: " << bar() << " " << goo << " " << 'x' << " " << false;
std::cout << std::endl << "Unordered call:";
foo(echo(bar()), echo(goo), echo('x'), echo(false));
std::cout << std::endl << "Ordered call:";
OrderedCall{ foo, echo(bar()), echo(goo), echo('x'), echo(false) };
std::cout << std::endl << "Try in Debug and Release modes. " << std::endl
<< "Ordered should match Occurance" << std::endl;
}
Видимо все еще не работает в последнем обновлении Visual Studio 19, в режиме отладки следующее:
Occurance: 10 Goo x 0
Unordered call: Goo 0 x 10
Ordered call: 0 x 10 Goo
Try in Debug and Release modes.
Ordered should match Occurance
См .: https://timsong-cpp.github.io/cppwp/n3337/dcl.init.list и https://timsong-cpp.github.io/cppwp/n3337/class.expl.init
В пределах списка инициализатора списка фигурных скобок предложения инициализатора, включая любые, которые являются результатом расширений пакета ([temp.variadic]), оцениваются в порядке, в котором они появляются. То есть каждое вычисление значения и побочный эффект, связанный с данным предложением инициализатора, упорядочивается перед каждым вычислением значения и побочным эффектом, связанным с любым предложением инициализатора, которое следует за ним в списке через запятую списка инициализатора. [Примечание: этот порядок оценки выполняется независимо от семантики инициализации; например, он применяется, когда элементы списка инициализатора интерпретируются как аргументы вызова конструктора, хотя обычно нет никаких ограничений последовательности аргументов вызова. - конец заметки]
[Вышеупомянутая заметка в тексте, не добавлена мной.]