هل لدى C ++ كلمة رئيسية "مع" مثل Pascal؟
-
21-09-2019 - |
سؤال
with
يمكن استخدام الكلمة الرئيسية في Pascal للوصول السريع إلى حقل السجل. أي شخص يعرف ما إذا كان 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 );
}
أو في C ++ 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()
على الرغم من أنني برمجة في الغالب في دلفي التي لديها 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 عضوًا في المؤشر أو متغير محلي أو أي شيء آخر. يتيح Pascal أيضًا العديد من المتغيرات مثل "مع var1 ، var2" مما يجعل الأمر أكثر صعوبة.
الأقرب الذي يمكنك الحصول عليه هو طريقة السلاسل:
myObj->setX(x)
->setY(y)
->setZ(z)
لوضع حقول متعددة و using
لمساحات الأسماء.
with (OBJECT) {CODE}
لا يوجد شيء من هذا القبيل في C ++.
يمكنك وضع التعليمات البرمجية كما هي في طريقة الكائن ، لكنها ليست مرغوبة دائمًا.
مع C ++ 11 ، يمكنك الاقتراب عن طريق إنشاء الاسم المستعار مع اسم قصير للكائن.
على سبيل المثال الرمز الوارد في السؤال ، سيبدو هكذا:
{
auto &_ = *pointer;
if (_.field1 && ... && _.fieldn) {...}
}
(يتم استخدام الأقواس المجعد المحيطة للحد من رؤية الاسم المستعار _
)
إذا كنت تستخدم بعض الحقل في كثير من الأحيان ، فيمكنك التلقائي مباشرة:
auto &field = pointer->field;
// Even shorter alias:
auto &_ = pointer->busy_field;
أولاً سمعت أن أي شخص لا يحب "مع". القواعد واضحة تمامًا ، لا تختلف عن ما يحدث داخل فئة في C ++ أو Java. ولا تتغاضى عن أنه يمكن أن يؤدي إلى تحسين برنامج التحويل البرمجي.
بعد أن كتبت العديد من المحللين ، يبدو أن قائمة بسيطة ميتة تبحث عن الكائن المسماة ، إما ثابتًا أو ديناميكيًا. علاوة على ذلك ، لم أر قط موقفًا لم يحدد فيه المترجم بشكل صحيح الكائن والنوع المفقود بشكل صحيح ، لذلك يبدو أن كل تلك الأعذار العرجاء لعدم السماح بـ ... endwith بناء الكثير من hooey. بالنسبة لبقية منا عرضة لأسماء الكائنات الطويلة ، فإن أحد الحلول هو إنشاء تعريفات بسيطة. لم أستطع المقاومة ، لنفترض أن لدي:
#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;
النهج التالي يعتمد على التعزيز. إذا كان المترجم الخاص بك يدعم C ++ 0x's auto
ثم يمكنك استخدام ذلك والتخلص من الاعتماد على التعزيز.
عدم اعطاء رأي: من فضلك لا تفعل ذلك في أي رمز يجب الحفاظ عليه أو قراءته من قبل شخص آخر (أو حتى بنفسك في غضون بضعة أشهر):
#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->...
...
}
تعاريف مبسطة غير محملة (اختر واحدة):
لم يذكر اسمه (أسلوب Kotlin 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 طوال اليوم وألغت هذا الأمر قبل أن يأخذني أي شخص إلى عمال النظافة. في برنامج أكبر ، هذا مثال على كيفية الحفاظ على عدد موثوق به لشيء ما.