Есть ли в C++ ключевое слово «with», как в Pascal?
-
21-09-2019 - |
Вопрос
with
Ключевое слово в Паскале можно использовать для быстрого доступа к полю записи.Кто-нибудь знает, есть ли в C++ что-нибудь подобное?
Бывший:У меня есть указатель со многими полями, и я не хочу вводить такое:
if (pointer->field1) && (pointer->field2) && ... (pointer->fieldn)
то, что мне действительно нужно, это что-то вроде этого на C++:
with (pointer)
{
if (field1) && (field2) && .......(fieldn)
}
Решение
В C++ вы можете поместить код в метод класса, на который ссылаетесь, с помощью pointer
.Там вы можете напрямую ссылаться на члены, не используя указатель.Сделай это inline
и вы в значительной степени получите то, что хотите.
Другие советы
Вероятно, самое близкое, что вы можете получить, это:(Пожалуйста, не ставьте мне минус;это всего лишь академическое упражнение.Конечно, вы не можете использовать какие-либо локальные переменные в теле этих искусственных with
блоки!)
struct Bar {
int field;
};
void foo( Bar &b ) {
struct withbar : Bar { void operator()() {
cerr << field << endl;
}}; static_cast<withbar&>(b)();
}
Или, немного более демонично,
#define WITH(T) do { struct WITH : T { void operator()() {
#define ENDWITH(X) }}; static_cast<WITH&>((X))(); } while(0)
struct Bar {
int field;
};
void foo( Bar &b ) {
if ( 1+1 == 2 )
WITH( Bar )
cerr << field << endl;
ENDWITH( b );
}
или в С++0x
#define WITH(X) do { auto P = &X; \
struct WITH : typename decay< decltype(X) >::type { void operator()() {
#define ENDWITH }}; static_cast<WITH&>((*P))(); } while(0)
WITH( b )
cerr << field << endl;
ENDWITH;
нет, такого ключевого слова нет.
Мне нравится использовать:
#define BEGIN_WITH(x) { \
auto &_ = x;
#define END_WITH() }
Пример:
BEGIN_WITH(MyStructABC)
_.a = 1;
_.b = 2;
_.c = 3;
END_WITH()
Несмотря на то, что я программирую в основном на Delphi, у которого есть with
ключевое слово (поскольку Delphi является производной от Pascal), я не использую with
.Как говорили другие:это немного экономит на наборе текста, но чтение усложняется.
В случае, подобном приведенному ниже коду, может возникнуть соблазн использовать with
:
cxGrid.DBTableView.ViewData.Records.FieldByName('foo').Value = 1;
cxGrid.DBTableView.ViewData.Records.FieldByName('bar').Value = 2;
cxGrid.DBTableView.ViewData.Records.FieldByName('baz').Value = 3;
С использованием with
это выглядит так
with cxGrid.DBTableView.ViewData.Records do
begin
FieldByName('foo').Value = 1;
FieldByName('bar').Value = 2;
FieldByName('baz').Value = 3;
end;
Я предпочитаю использовать другой метод, вводя дополнительную переменную, указывающую на то же самое. with
указывал бы на.Так:
var lRecords: TDataSet;
lRecords := cxGrid.DBTableView.ViewData.Records;
lRecords.FieldByName('foo').Value = 1;
lRecords.FieldByName('bar').Value = 2;
lRecords.FieldByName('baz').Value = 3;
Таким образом, нет никакой двусмысленности, вы немного экономите на вводе, и цель кода становится яснее, чем при использовании with
Нет, в C++ такого ключевого слова нет.
В C++ такой возможности нет.И многие считают «С» в Паскале проблемой, поскольку это может сделать код неоднозначным и трудным для чтения, например, трудно определить, является ли поле1 членом указателя, локальной переменной или чем-то еще.Паскаль также допускает использование нескольких переменных with, например «With Var1,Var2», что еще больше усложняет задачу.
Самое близкое, что вы можете получить, это цепочка методов:
myObj->setX(x)
->setY(y)
->setZ(z)
для установки нескольких полей и using
для пространств имен.
with (OBJECT) {CODE}
В С++ такого нет.
Вы можете поместить CODE как есть в метод OBJECT, но это не всегда желательно.
С C++11 вы можете приблизиться к этому, создав псевдоним с коротким именем для ОБЪЕКТА.
Например, приведенный в вопросе код будет выглядеть так:
{
auto &_ = *pointer;
if (_.field1 && ... && _.fieldn) {...}
}
(Окружающие фигурные скобки используются для ограничения видимости псевдонима. _
)
Если вы часто используете какое-то поле, вы можете напрямую присвоить ему псевдоним:
auto &field = pointer->field;
// Even shorter alias:
auto &_ = pointer->busy_field;
Впервые я услышал, что никто не любит «с».Правила совершенно просты и ничем не отличаются от того, что происходит внутри класса в C++ или Java.И не упускайте из виду, что это может привести к значительной оптимизации компилятора.
После написания множества парсеров это кажется мертвенно простым списком для поиска именованного объекта, статического или динамического.Более того, я никогда не видел ситуации, когда компилятор неправильно идентифицировал отсутствующий объект и тип, поэтому все эти неубедительные оправдания запрета конструкции With ...ENDWITH кажутся ерундой.Для остальных из нас, склонных к длинным именам объектов, обходным путем является создание простых определений.Не удержался, предположим, что у меня есть:
#include<something>
typedef int headache;
class grits{
public:
void corn(void);
void cattle(void);
void hay(void);}; //insert function defs here
void grits::grits(void)(printf("Welcome to Farm-o-mania 2012\n");};
#define m mylittlepiggy_from_under_the_backporch.
headache main(){
grits mylittlepiggy_from_under_the_backporch;
m corn(); //works in GCC
m cattle();
m hay();
return headache;
Следующий подход основан на Boost.Если ваш компилятор поддерживает C++0x auto
тогда вы сможете использовать это и избавиться от зависимости Boost.
Отказ от ответственности:пожалуйста, не делайте этого ни в каком коде, который должен поддерживаться или читаться кем-то другим (или даже вами через несколько месяцев):
#define WITH(src_var) \
if(int cnt_ = 1) \
for(BOOST_AUTO(const & _, src_var); cnt_; --cnt_)
int main()
{
std::string str = "foo";
// Multiple statement block
WITH(str)
{
int i = _.length();
std::cout << i << "\n";
}
// Single statement block
WITH(str)
std::cout << _ << "\n";
// Nesting
WITH(str)
{
std::string another("bar");
WITH(another)
assert(_ == "bar");
}
}
Я вижу один случай, когда «с» действительно полезно.
В методах для рекурсивных структур данных часто бывает так:
void A::method()
{
for (A* node = this; node; node = node->next) {
abc(node->value1);
def(value2); // -- oops should have been node->value2
xyz(node->value3);
}
}
ошибки, вызванные такими опечатками, очень трудно найти.
С помощью «с» вы можете написать
void A::method()
{
for (A* node = this; node; node = node->next) with (node) {
abc(value1);
def(value2);
xyz(value3);
}
}
Вероятно, это не перевешивает все остальные минусы, упомянутые для слова «с», но просто как интересная информация...
Возможно, вы сможете:
auto p = *pointer;
if (p.field1) && (p.field2) && ... (p.fieldn)
Или создайте небольшую программу, которая поймет with
операторы на C++ и перевести их в ту или иную форму допустимого C++.
Нет, нет with
Ключевое слово в C/C++.
Но вы можете добавить его с помощью кода препроцессора:
/* Copyright (C) 2018 Piotr Henryk Dabrowski, Creative Commons CC-BY 3.0 */
#define __M2(zero, a1, a2, macro, ...) macro
#define __with2(object, as) \
for (typeof(object) &as = (object), *__i = 0; __i < (void*)1; ++__i)
#define __with1(object) __with2(object, it)
#define with(...) \
__M2(0, ##__VA_ARGS__, __with2(__VA_ARGS__), __with1(__VA_ARGS__))
Использование:
with (someVeryLongObjectNameOrGetterResultOrWhatever) {
if (it)
it->...
...
}
with (someVeryLongObjectNameOrGetterResultOrWhatever, myObject) {
if (myObject)
myObject->...
...
}
Упрощенные неперегруженные определения (выберите одно):
безымянный (стиль Котлина it
):
#define with(object) \
for (typeof(object) &it = (object), *__i = 0; __i < (void*)1; ++__i)
названный:
#define with(object, as) \
for (typeof(object) &as = (object), *__i = 0; __i < (void*)1; ++__i)
Конечно for
Цикл всегда имеет только один проход и будет оптимизирован компилятором.
#include <iostream>
using namespace std;
template <typename T>
struct with_iter {
with_iter( T &val ) : p(&val) {}
inline T* begin() { return p; }
inline T* end() { return p+1; }
T *p;
};
#define with( N, I ) for( auto &N : with_iter<decltype(I)>(I) )
int main() {
with( out , cout ) {
out << "Hello world!" << endl;
}
return 0;
}
Нуф сказал...
Простой способ сделать это заключается в следующем.
class MyClass
{
int& m_x;
public MyClass(int& x)
{
m_x = x;
m_x++;
}
~MyClass()
{
m_x--;
}
}
int main():
{
x = 0;
{
MyClass(x) // x == 1 whilst in this scope
}
}
Я целый день писал на Python и просто забросил это, прежде чем кто-нибудь отнес меня в химчистку.В более крупной программе это пример того, как вести надежный подсчет чего-либо.