Конструктор не может быть вызван для ограничений содержащих инструкции препроцессора

Обновлено: 16.05.2024

« Как стать программистом 1С » Язык 1С » Препроцессор 1С и компилятор 1С

Препроцессор 1С и компилятор 1С

После написания программы на встроенном языке 1С она сохраняется в конфигурацию в составе модуля. При запуске 1С в режиме 1С Предприятие программа на языке 1С будет выполнена.

Сначала немного терминов:

    — специальная программа, которая перерабатывает программный код из «вида» удобного для работы программиста, в «вид», удобный для работы копилятора; — специальная программа, которая умеет перерабатывать программный код в «машинный» код — выполняемый непосредственно процессором компьютера; — специальная программа, которая вместо компилирования кода в машинный код для процессора, выполняет его самостоятельно. Интерпретатор с предварительной компиляцией — компилирует программу не в машинный код, а в специальный «байт-код» удобный для последующего выполнения интерпретатором.

Как выполняются программы написанные на встроенном языке 1С?

Программа на языке 1С перед выполнением компилируется – преобразовывается в специальный код.

Выполнение кода на языке 1С производится в три этапа:

  • Обработка модулей препроцессором 1С согласно директив препроцессора 1С
  • Компиляция в байт-код
  • Исполнение

Компиляция производится отдельно на клиенте и отдельно на сервере (даже одного и того же модуля), при первом обращении к нему.

Компилятор 1С на входе получает модуль не в том виде, в каком его видит программист. Препроцессор 1С разрезает модуль на части (вырезая не нужное) и потом соединяет его.

Директива препроцессора 1С — это способ указать препроцессору 1С где будет выполняться указанный участок кода на языке 1С.

Это связано с тем, что выполнение текста программы производится на сервере и на клиенте. Есть функции и процедуры, которые не могут быть выполнены на сервере/клиенте. Например на сервере Вы не можете показать пользователю предупреждение с необходимостью нажатия кнопки ОК.

Поэтому в модуле указывается где должен выполняться код:

  • Общий модуль (ветка Общие/Общие модули) – в свойствах модуля указывается может ли он выполняться на сервере и на клиенте
  • В остальных модулях – для этого используются директива препроцессора 1С.

Непосредственно в тексте модуля, блоки программного кода, отмечаются директивы препроцессора 1С:
Функция Пример1() //будет выполнена и на клиенте и на сервере
КонецФункции

Если никаких инструкций препроцессору 1С в тексте не указано, и использована функция, которую нельзя выполнять на сервере/клиенте, то в момент компилирования модуля (при первом доступе к нему) в исполняемом режиме будет вызвана ошибка.

«Обертывать» можно не только функции, но и конкретные строки исполняемого кода.

Так как компиляция 1С на данный момент еще не началась, то можно с помощью таких блоков создавать функции с одинаковыми наименованиями (для сервера, для клиента).

В модуле управляемой формы инструкции препроцессору 1С рекомендуется использовать только внутри функций/процедур.

После того, как препроцессор 1С «склеил» модуль, он передает его компилятору 1С, который его компилирует. Далее в режиме исполнения код будет выполнен.

При выполнении кода одного модуля, [может] происходит разовое/множественное переключения выполнения с клиента на сервер и обратно.

Например, если требуется выполнить запрос к базе данных, то выполнение будет переключено на сервер, выполнен запрос, данные переданы на клиент. Таким образом модуль существует на сервере и на клиенте.

Переключение исполнения с клиента на сервер и обратно производится «автоматически».

По умолчанию толстый клиент выполняет весь код на клиенте и иногда вызывает сервер. Тонкий клиент наоборот – все выполняет на сервере и иногда вызывает клиент (хотя в любом случае инициализация первого вызова сервера производится клиентом).

Программист в получившемся «склеенном» модуле может для каждой функции указать, где ее требуется исполнять. Не забываем, что доступ к данным производится на сервере, а инициализация вызова на клиенте. Например:
&НаСервере
Функция ПолучитьДанныеБазыДанных()
Запрос = Новый Запрос("");
КонецФункции

Ранее, в толстом клиенте, форма создавалась и была доступна только на клиенте (если не передать ее параметром на сервер, конечно). Управляемая форма создается на сервере и может обрабатываться на клиенте и на сервере.

Поэтому, при выполнении функций модуля, при переключении выполнения с клиента на сервер и обратно, передаются кроме прочего все данные формы (называется «контекст»).

Данных может быть много и передаваться они будут «долго». А в вызываемой функции они могут быть и не нужны, она их не использует вовсе. Для таких случаев есть директива &НаСервереБезКонтекста.

Начальные данные:
1. Платформа 8.2.15
2. Конфигурация самописная для управляемого приложения.

Есть общий модуль, у которого поставлены следующие галки: Клиент, Сервер, Вызов сервера.
В этом модуле расположена следующая функция:

При этом 1с пишет, что функция "Получить()" не обнаружена.
На сколько я понимаю, содержимое общего модуля в соответствии с установленными галками компилируется как на стороне клиента, так и на стороне сервера, при этом инструкцией препроцессора я указал, что функция "Получить()" должна быть скомпилирована только на стороне сервера (на клиенте она мне не нужна), при этом я пытаюсь вызвать ее со стороны клиента на что мне дает право галка "Вызов сервера".

Что я делаю не так, почему 1с не видит эту функцию?

Потому, что инструкцией препроцессора Вы блокировали объявление этой функции на клиенте. Перед вызовом функции с клиента Вам необходимо передать исполнение на сервер.

(2) asved.ru, к сожалению, под рукой у меня сейчас документации по платформе нет, но на сколько я помню, галка "Вызов сервера" управляет доступностью серверных функций, расположенных в общем модуле, для их вызова из клиентских процедур.
Если у общего модуля я уберу галку "Клиент" - серверная функция вызывается без проблем, т.е. тут вопрос в том, почему с установленной галкой "Клиент" функция невидима (т.е. 1с не пытается вызвать ее на сервере, хотя она там присутствует), а при отключенной галке - функция становится видна.
На форумах я единственное что вычитал только то, что это особенность работы модулей форм, и особого внятного ответа на этот вопрос не нашел, т.е. я так понимаю, что в общем модуле с тремя установленными галками "Клиент", "Сервер" и "Вызов сервера" использование инструкций препроцессора и директив компиляции смысла не имеет.

(3) Zigfridish, мои 5 копеек. :)

Суть галочек в следующем:
Галочки "Клиент" и "Сервер" - означают что общий модуль может вызываться и из клиентской части и из сервеной. И помоему там диркетивы (&Клиент или &Сервер) не важны (просто из соответсвующего места должны вызываться соотвествующие процедуры) и при помоще интсрукции компиляции

разрешаются всякие колизии связанные с вызовом процедур.

Галочка "ВызовСервере" действительно позволяет выполнять вызов процедур из клиентской части, но выполняться они при этом будут на серевере. Т.е. можно поставить галочки Сервер и Вызов и споконо вызывать ее из клиента.

Вобщем попробуй убрть галочку Клиент. Тогда будет вызываться сервер и инстукция отработает нормально

Кажется так, если не путаю (у меня тоже нет документации под рукой :)

(4) Ягг, если я оставлю только две галки "Сервер" и "Вызов сервера" - смысл использования директив компиляции и инструкций в общем модуле пропадает, т.к. он весь полностью будет скомпилирован на сервере.
Так вот мне и хочется, чтобы в модуле присутствовали как клиентские, так и серверные процедуры, и чтобы я мог их разграничить с помощью инструкций препроцессора (часть из них скомпилировать на сервере, часть - на клиенте, а некоторые из них - на обеих сторонах).

(6) Zigfridish, тогда оставь обе галки, но суть в том что если модуль будет вызываться из клиентсокого контектса, то нужно вызывать только доступные процедуры. и наоборот.

В товвоем случае ты вызываешь общий модуль из клиента, при этом он и считается выполняемым на клиенте. Раз так то инструкция "Если сервер" естесвенно счиатет что это не сервер (это клиентский контекст) и не компилирует кусок.

Вобщем, тогда оставиль Клиент и Сервер и убери инструкцию препроцессора. Но учти, что процедура вэтом случае выполняется имено в клиентском контексте.

(9) Ягг, дело в том, что эта функция общего модуля не может исполняться на клиенте (у меня в ней используется запрос к базе), поэтому я ее и оградил инструкцией, чтобы она присутствовала и исполнялась только на сервере, и чтобы ее можно было вызвать из клиентской части (галка "Вызов сервера").
Помню даже в какой-то желтой книге видел подобную фразу по последовательности действий, которые выполняет 1с при вызове серверной функции с клиента: "сначала производится поиск вызываемой функции на клиенте, т.к. на клиенте она отсутствует - производится ее поиск на сервере, после чего на сервере происходит ее исполнение и по окончании процесса управление передается на клиента". Вот как то так

(10) Zigfridish, эээ. боюсь так не выйдет. Если стоит галочка "Клиент" и модуль вызывается из клиента, то априори считается что общий модуль выполняется в контексте клиента (система сама определит контекст по месту вызова). Тогда надо переносить какую-ту часть процедуры другой общий модуль. Другому модулю ставить галочку "Сервер" и "Вызов сервера" и на него уже ссылаться.

Может конечно меня кто поправит но по моему так.

ЗЫ. Допишу что бы не было разночтений (еще раз). Если стоят обе галки, о контекст будет определяться по месту вызова.

(12) Zigfridish, вообще, есть объекты "Команды" (Общие команды) она в какой-то степени имеет и серверную и клиентскую часть (место выполнения по директиве &НаСервере и т.д.). Но вобще-то они (КОМАНДЫ) для этого не предназначены и будут заморочки с передачей параметров и прочим.

Но попробуй, может найдется какое-то приемлимое рещение, если очень охото все вместе, но по хорошему лучше разносить :)

В догонку: можно еще попробовать использовать какую-нибуь "формальную" управляемую форму. С ней пожалуй меньше возьни чем с Командой в данном случае.

(6)Так может правильней сделать два общих модуля с разным набором галочек?
Если запуск инструкций все равно разграничиваешь командами препроцессора.

(5) red80, я эту книгу читал, и насколько помню, то в ней не рассматриваются примеры использования инструкций препроцессора в общих модулях.


Почему же тогда при отключенной галке "Клиент" предварительная передача исполнения на сервер в модуле формы не требуется (я могу напрямую из клиентской процедуры формы обратиться к серверной функции общего модуля)?

когда пытался собрать их конструктором, который там есть для ограничений, то 1с выдала предупреждение: запрос не может быть собран из-за наличия в нем инструкции препроцесору.

т.е. это два реквизита регистра: Контрагент и Организация.. они проверяются на соответствие Контрагенту и организации каким значениям?
не совсем понимаю..

(3) В конфигураторе в ролях есть закладка "Шаблоны". Как это работает можно посмотреть там.
В двух словах. У пользователя назначаются права на доступ к записям. Не назначены права - нет доступа. Права могут быть назначены группе, а пользователь может входить в эту группу или может быть использована схема профилей (УПП). При этом может быть указано по каким объектам выполняется контроль доступа на уровне записей. Т.е. можно контролировать доступ, например, только по организации.

надо подчеркнуть, что с шаблонами ограничения записей нужно быть очень осторожным. если у пользователя назначены две роли и в каждой из них наложены ограничения на уровне записей, могут быть непредсказуемые последствия. 1С немного намутила с этими шаблонами.

Почему не предсказуемые? Все нормально. А везде в запросах для таких пользователей надо использовыть "ВЫБРАТЬ РАЗРЕШЕННЫЕ"

например, если в одной роли для объекта справочника определена следующее ограничение на чтение:
Прочие поля : ГДЕ Ложь

а в другой роли на этот объект наложено ограничение по шаблону, в итоге будем иметь ошибку доступа при попытке програмно обратиться к этому справочнику, чего не наблюдается в регистрах накопления. это как пример.

нужно очень осторожно настраивать роли для пользователя.

>нужно очень осторожно настраивать роли для пользователя.

Трудно не согласиться.

Лучше настраивать 1 роль, чем комбинировать несколько, тем более на RLS

2(9): еще RLS очень "тормозит" динамические списки .

например, у меня была задача, нужно в заказах покупателей показывать, введен ли на основании заказа Счет или Расходная накладная. Вроде бы делаю через КритерииОтбора, но тормоза жуткие, если список фильтруется с помощью RLS.

Обновлено: Очевидно, вы захотите сделать это с помощью шаблонов или базового класса, а не макросов. К сожалению, по разным причинам я не могу использовать ни шаблоны, ни базовый класс.

На данный момент я использую макрос для определения множества полей и методов в различных классах, например:

FIELDS_AND_METHODS - это многострочный макрос, в котором используются операторы преобразования строк и вставки токенов.

Я хотел бы заменить это на следующие вещи

Однако у меня возникают проблемы с «преобразованием в строку» и «вставкой токена» символа препроцессора TYPE_NAME в файл FieldsNMethods.h .

Например, я хочу определить деструктор класса в FieldsNMethods.h , поэтому для этого нужно будет использовать значение TYPE_NAME , как показано ниже:

Но с TYPE_NAME заменено его значение.

Возможно ли то, что я пытаюсь сделать? Я не могу напрямую использовать операторы преобразования строк и вставки токенов, потому что я не участвую в макроопределении.

Пытались ли вы когда-нибудь заполнить веб-форму в области электронной коммерции, которая требует много кликов и выбора? Вас попросят заполнить дату.

Будучи разработчиком веб-приложений, легко впасть в заблуждение, считая, что приложение без JavaScript не имеет права на жизнь. Нам становится удобно.

Если вы ищете пакет для быстрой интеграции календаря с выбором даты в ваше приложения, то библиотека Flatpickr отлично справится с этой задачей.

Клиент для URL-адресов, cURL, позволяет взаимодействовать с множеством различных серверов по множеству различных протоколов с синтаксисом URL.

У каждого из нас бывали случаи, когда нам нужно отцентрировать блочный элемент, но мы не знаем, как это сделать. Даже если мы реализуем какой-то.

Ответы 4

Это требует шаблона.

Прямой ответ на последнюю часть вашего вопроса - «учитывая, что я больше не участвую в макроопределении, как заставить работать операторы вставки и преобразования строк» ​​- это «Вы не можете». Эти операторы работают только в макросах, поэтому вам придется писать вызовы макросов, чтобы заставить их работать.

@mackenir сказал, что "шаблоны не подходят". Почему шаблоны не подходят? Код имитирует шаблоны старомодным предварительным стандартом, предварительно шаблонным способом, и это вызывает много боли и горя. Использование шаблонов позволит избежать этой боли - хотя потребуется операция преобразования.

@mackenir спросил: "Есть ли способ заставить все работать с макросами?" Да, можно, но вам следует использовать шаблоны - они более надежны и удобны в обслуживании. Чтобы он работал с макросами, вам нужно, чтобы имена функций в коде во включенном заголовке были вызовами макросов. Чтобы это работало правильно, вам нужно пройти определенный уровень косвенного обращения:

Этот уровень косвенного обращения часто является необходимой идиомой как для токенизирующих, так и для строковых операторов.

Дополнительные комментарии от @mackenir указывают на продолжающиеся проблемы. Давайте конкретизируем.

At the moment I am using a macro to define a bunch of fields and methods on various classes, like this:

FIELDS_AND_METHODS is a multi-line macro that uses stringizing and token-pasting operators.

I would like to replace this with the following kind of thing

В ПОРЯДКЕ. Чтобы сделать это конкретным, нам нужен макрос FIELDS_AND_METHODS(type) , который является многострочным и использует вставку токенов (я не собираюсь заниматься структурированием - тем не менее, будут применяться те же базовые механизмы).

Если повезет, он объявляет член типа «указатель на тип аргумента», конструктор для этого типа и метод (в данном случае Example_next), который возвращает этот указатель.

Содержимое fieldsNmethods.h становится следующим:

Обратите внимание, что заголовок не будет содержать защиты множественного включения; его смысл в том, чтобы позволить включать его несколько раз. Он также отменяет определение своих вспомогательных макросов, чтобы разрешить множественное включение (ну, поскольку переопределения будут идентичными, они «мягкие» и не вызовут ошибки), и я добавил к ним префикс FNM_ в качестве примитивного элемента управления пространством имен для макросов. Это генерирует код, который я ожидал от препроцессора C. а G ++ не светится, а создает пустой объектный файл (потому что объявленные типы не используются в моем примере кода).

Обратите внимание, что для этого не требуется никаких изменений в вызывающем коде, кроме указанного в вопросе. Я думаю, что вопрос следует улучшить, используя принцип SPOT "Single Point of Truth" (или DRY "Don't Repeat Yourself"):

Спасибо. Создание шаблона - это не вариант. Итак, вы подразумеваете, что есть способ достичь того же эффекта, что и операторы преобразования строк и вставки токенов в FieldsNMethods.h?

Классы являются общедоступным API, и мы не хотим загрязнять их, создавая шаблоны или имея базовый класс. Кроме того, это классы C++ / CLI, поэтому частное наследование не вариант. Спасибо за дополнительную информацию, я попробую.

«загрязнять» я имею в виду «раскрыть детали реализации таким образом, чтобы они могли запутать пользователей API».

а вы для этого используете макросы? это звучит злобно! Разве вы не можете написать такой фасад, который будет предоставлять клиенту только то, что вы хотите, и иметь внутреннюю поддержку?

Мы не хотим вводить много уровней косвенного обращения, и нам все равно понадобятся макросы для передачи во внутреннюю реализацию (или иначе придется писать много одного и того же кода много раз).

Нет, вы не можете определять определения классов или функций на лету. Они должны быть указаны либо путем непосредственного ввода, либо путем определения в препроцессоре.

Как правило, нет необходимости создавать подобные классы, и определения классов создаются перед компиляцией, будь то путем ввода всего текста или с помощью какой-либо генерации кода. Иногда существует отдельный этап генерации кода (например, в текущей Visual Studio вы можете определить этапы предварительной и последующей обработки).

Теперь, если вам нужно создать разные версии некоторых классов для разных типов данных, вы должны использовать шаблоны. Таким образом нельзя создавать штампы классов с разными именами.

Последний вопрос: зачем вы это делаете? Я никогда не был в ситуации, когда что-то подобное выглядело бы полезным в C++, и на языках, где это имеет смысл, есть средства для этого.

Я храню этот класс в QVector , поэтому, к сожалению, мне нужно добавить конструктор по умолчанию. Что касается дизайна программного обеспечения, это нехорошо для меня (потому что у меня есть ограничение на uiIndex ). Есть ли способ разрешить только QT ( QVector в моем случае) вызывать конструктор по умолчанию? Макрос или инструкция препроцессора?

Как насчет использования std::vector вместо этого? В противном случае стоит подумать о создании истории для std::optional или какого-либо другого типа прокси.

@ FrançoisAndrieux Я не могу, это полноценное приложение Qt.

Напоминает мне очень старый анекдот: «Доктор, мне больно, когда я это делаю (пациент делает очень странный жест рукой и ухом). Что вы можете предложить? И хороший доктор: я предлагаю этого не делать ».

Что означает «полный Qt»? Кто-то заставляет вас использовать QVector?

@Milleras Вы не можете использовать стандартную библиотеку C++?

@ FrançoisAndrieux Это бессмысленно. std::vector отлично подойдет для нестандартного конструктивного типа.

Kuba hasn't forgotten Monica

@ FrançoisAndrieux std :: optional работает только для C++ 17. К сожалению для меня, мне приходится использовать C++ 14.

Пытались ли вы когда-нибудь заполнить веб-форму в области электронной коммерции, которая требует много кликов и выбора? Вас попросят заполнить дату.

Будучи разработчиком веб-приложений, легко впасть в заблуждение, считая, что приложение без JavaScript не имеет права на жизнь. Нам становится удобно.

Если вы ищете пакет для быстрой интеграции календаря с выбором даты в ваше приложения, то библиотека Flatpickr отлично справится с этой задачей.

Клиент для URL-адресов, cURL, позволяет взаимодействовать с множеством различных серверов по множеству различных протоколов с синтаксисом URL.

У каждого из нас бывали случаи, когда нам нужно отцентрировать блочный элемент, но мы не знаем, как это сделать. Даже если мы реализуем какой-то.

Ответы 2

Вы можете попробовать сделать QVector классным другом.

Рассмотрим следующий пример игрушки:

При этом можно хранить экземпляры Foo в QVector :

Однако следующее будет, например, неудача:

Конструктор без аргументов не обязательно должен быть явным.

Отличная идея! Работает, спасибо. Я добавил InputField() = default; и friend class QVector;

@SergeyA: Спасибо за подсказку ;-) Я обновлю ответ.

@Milleras Помните, что это означает, что теперь можно получить доступ к созданным по умолчанию экземплярам с помощью QVector , что означает, что вы все равно можете использовать недопустимый экземпляр. А в нынешнем виде обнаружить этот случай не всегда просто.

Это решение тесно связывает Foo с QVector , не говоря уже о существовании конструктора по умолчанию - возможно, Foo использует нестандартные конструктивные элементы? Цель состоит в том, чтобы сделать так, чтобы QVector мог использовать не устанавливаемый по умолчанию Foo , без каких-либо изменений в Foo . Это ясно указывает на то, каким должно быть решение: специализация членов QVector для некопируемого типа Foo .

Kuba hasn't forgotten Monica

@ FrançoisAndrieux Да, это хороший аргумент. Но пока другого решения своей проблемы я не вижу :(

QVector не должен вызывать конструктор по умолчанию; В конце концов, std::vector этого не делает - если только вы не используете один из его методов, которому нужен этот конструктор. Все, что вам нужно, это отключить конструкцию по умолчанию в QVector :

Затем поместите макрос в то же место, где вы поместили Q_DECLARE_METATYPE : прямо там, где объявлен тип:

Затем вы можете использовать QVector с теми же ограничениями, что и std::vector .

Вы предлагаете расширить QVector? Иначе, как я могу использовать ваш макрос?

@JosephGarnier Макрос предоставляет специализации методов QVector . В этом прелесть шаблонных типов: вы можете легко заменить их кусочки и детали в особых обстоятельствах!

Kuba hasn't forgotten Monica

Вы сделали то, чего я не делал выше, потому что все работает нормально. Напомним, что такие специализации должны происходить в области пространства имен, а не в области класса. Фактически, вам нужно специализироваться на пространстве имен Qt, если таковое имеется. В противном случае в глобальном масштабе.

Kuba hasn't forgotten Monica

Восстановите свой проект. Как в: удалите папку сборки и снова выполните сборку. Способ устранения ошибок компоновщика всегда начинается с восстановления с чистого листа.

Kuba hasn't forgotten Monica

Очистить мой проект ничего не изменилось, но я нашел ошибку. Вы должны добавить inline перед возвращаемым типом в ваших функциях. В остальном отлично работает! Благодарить.

Читайте также: