ماكرو "قيم foreach" في دول مجلس التعاون الخليجي وCPP
سؤال
لدي ماكرو "foreach" أستخدمه بشكل متكرر في C++ والذي يعمل مع معظم حاويات STL:
#define foreach(var, container) \
for(typeof((container).begin()) var = (container).begin(); \
var != (container).end(); \
++var)
(لاحظ أن 'typeof' هو امتداد لدول مجلس التعاون الخليجي.) ويتم استخدامه على النحو التالي:
std::vector< Blorgus > blorgi = ...;
foreach(blorgus, blorgi) {
blorgus->draw();
}
أود أن أصنع شيئًا مشابهًا يتكرر على قيم الخريطة.ربما أطلق عليها اسم "foreach_value".لذا بدلاً من الكتابة
foreach(pair, mymap) {
pair->second->foo();
}
أود أن أكتب
foreach_value(v, mymap) {
v.foo();
}
لا يمكنني التوصل إلى ماكرو يقوم بذلك، لأنه يتطلب الإعلان عن متغيرين:المكرر ومتغير القيمة ('v'، أعلاه).لا أعرف كيفية القيام بذلك في مُهيئ حلقة for، حتى باستخدام امتدادات gcc.يمكنني أن أعلن ذلك قبل استدعاء foreach_value مباشرة، ولكنه سيتعارض بعد ذلك مع مثيلات أخرى من الماكرو foreach_value في نفس النطاق.إذا كان بإمكاني إضافة رقم السطر الحالي إلى اسم متغير التكرار، فسيعمل ذلك، لكنني لا أعرف كيفية القيام بذلك.
المحلول
يمكنك القيام بذلك باستخدام حلقتين.يعلن الأول عن المكرر، باسم دالة لمتغير الحاوية (ويمكنك جعل هذا أقبح إذا كنت قلقًا بشأن التعارضات مع التعليمات البرمجية الخاصة بك).والثاني يعلن متغير القيمة.
#define ci(container) container ## iter
#define foreach_value(var, container) \
for (typeof((container).begin()) ci(container) = container.begin(); \
ci(container) != container.end(); ) \
for (typeof(ci(container)->second)* var = &ci(container)->second; \
ci(container) != container.end(); \
(++ci(container) != container.end()) ? \
(var = &ci(container)->second) : var)
باستخدام نفس شرط إنهاء الحلقة، تحدث الحلقة الخارجية مرة واحدة فقط (وإذا كنت محظوظًا، فسيتم تحسينها).يمكنك أيضًا تجنب الاتصال بـ ->الثانية على المُكرِّر إذا كانت الخريطة فارغة.وهذا هو نفس السبب وراء زيادة المشغل الثلاثي للحلقة الداخلية؛في النهاية، نترك var عند القيمة الأخيرة، حيث لن تتم الإشارة إليها مرة أخرى.
يمكنك تضمين ci(container)، لكنني أعتقد أنه يجعل الماكرو أكثر قابلية للقراءة.
نصائح أخرى
سوف تبحث عنه BOOST_FOREACH - لقد قاموا بكل العمل من أجلك بالفعل!
إذا كنت تريد أن تدور حولها ، فيمكنك إعلان كتلة في أي مكان في C ++ ، والتي تحل مشكلة النطاق الخاصة بك مع التخزين المتوسط الخاص بك من ITR-> الثانية ...
// Valid C++ code (which does nothing useful)
{
int a = 21; // Which could be storage of your value type
}
// a out of scope here
{
int a = 32; // Does not conflict with a above
}
المحكمة الخاصة بلبنان تحول تقوم الوظيفة أيضًا بشيء مماثل.
الحجج هي (بالترتيب):
- مكرر الإدخال الذي يحدد بداية الحاوية
- مكرر الإدخال الذي يحدد نهاية الحاوية
- مكرر إخراج يحدد مكان وضع المخرجات (بالنسبة للتحويل في المكان، على غرار for-each، ما عليك سوى تمرير مكرر الإدخال في رقم 1)
- دالة أحادية (كائن دالة) يتم تنفيذها على كل عنصر
للحصول على مثال بسيط للغاية، يمكنك تكبير كل حرف في سلسلة من خلال:
#include <iostream>
#include <string>
#include <algorithm>
#include <cctype>
int main(int argc, char* argv[]) {
std::string s("my lowercase string");
std::transform(s.begin(), s.end(), s.begin(), toupper);
std::cout << s << std::endl; // "MY LOWERCASE STRING"
}
بدلا من ذلك هناك أيضا جمع وظيفة، والتي تسمح بالاحتفاظ ببعض القيم بين استدعاءات كائن الوظيفة. جمع لا يقوم بتعديل البيانات الموجودة في حاوية الإدخال كما هو الحال مع تحول.
هل فكرت في استخدام تعزيز المكتبات؟لديهم foreach
تم تنفيذ الماكرو والذي ربما يكون أقوى من أي شيء ستكتبه ...وهناك أيضا transform_iterator
والذي يبدو أنه يمكن استخدامه للقيام بالجزء الثاني من الاستخراج لما تريده.
لسوء الحظ لا أستطيع أن أخبرك بالضبط كيف لاستخدامه لأنني لا أعرف ما يكفي من C++ :) هذا البحث جوجل يظهر بعض الإجابات الواعدة: comp.lang.c++.moderated, تعزيز حالة استخدام Transform_iterator.
Boost::For_each هو أفضل رهان لك على الإطلاق.الشيء الأنيق هو أن ما يقدمونه لك بالفعل هو الماكرو BOOST_FOREACH() والذي يمكنك بعد ذلك لفه و#تعريفه إلى ما تريد تسميته حقًا في التعليمات البرمجية الخاصة بك.سيختار معظم الأشخاص "foreach" القديمة الجيدة، ولكن قد يكون لدى المتاجر الأخرى معايير ترميز مختلفة، لذلك يتناسب هذا مع تلك العقلية.لدى Boost أيضًا الكثير من الأشياء الجيدة الأخرى لمطوري C++!تستحق الاستخدام.
لقد قمت بإنشاء مساعد Foreach.h صغير مع عدد قليل من المتغيرات لـ foreach() بما في ذلك كلا النوعين اللذين يعملان على المتغيرات المحلية وعلى المؤشرات، بالإضافة إلى إصدار إضافي مؤمن ضد حذف العناصر من داخل الحلقة.لذا فإن الكود الذي يستخدم وحدات الماكرو الخاصة بي يبدو لطيفًا ومريحًا مثل هذا:
#include <cstdio>
#include <vector>
#include "foreach.h"
int main()
{
// make int vector and fill it
vector<int> k;
for (int i=0; i<10; ++i) k.push_back(i);
// show what the upper loop filled
foreach_ (it, k) printf("%i ",(*it));
printf("\n");
// show all of the data, but get rid of 4
// http://en.wikipedia.org/wiki/Tetraphobia :)
foreachdel_ (it, k)
{
if (*it == 4) it=k.erase(it);
printf("%i ",(*it));
}
printf("\n");
return 0;
}
انتاج:
0 1 2 3 4 5 6 7 8 9
0 1 2 3 5 6 7 8 9
لي Foreach.h يوفر وحدات الماكرو التالية:
- foreach() - foreach العادية للمؤشرات
- foreach_() - foreach_ منتظم للمتغيرات المحلية
- foreachdel() - إصدار foreach مع التحقق من الحذف داخل إصدار الحلقة والمؤشر
- foreachdel_() - إصدار foreach مع التحقق من الحذف داخل الحلقة، إصدار المتغير المحلي
من المؤكد أنهم يعملون لصالحي، وآمل أن يجعلوا حياتك أسهل قليلاً :)
هناك جزأين لهذا السؤال.تحتاج إلى (1) إنشاء مكرر بطريقة أو بأخرى (أو بالأحرى تسلسل قابل للتكرار) فوق خريطتك قيم (وليس المفاتيح)، و(2) استخدم ماكرو لإجراء التكرار دون الكثير من القواعد المعيارية.
الحل الأنظف هو استخدام أ نطاق التعزيز مشترك كهربائي للجزء (1) و تعزيز البحث للجزء (2).لا تحتاج إلى كتابة الماكرو أو تنفيذ المكرر بنفسك.
#include <map>
#include <string>
#include <boost/range/adaptor/map.hpp>
#include <boost/foreach.hpp>
int main()
{
// Sample data
std::map<int, std::string> myMap ;
myMap[0] = "Zero" ;
myMap[10] = "Ten" ;
myMap[20] = "Twenty" ;
// Loop over map values
BOOST_FOREACH( std::string text, myMap | boost::adaptors::map_values )
{
std::cout << text << " " ;
}
}
// Output:
// Zero Ten Twenty
يمكنك تحديد فئة القالب التي تأخذ نوع mymap كمعلمة قالب، وتعمل كمكرر على القيم عن طريق التحميل الزائد * و->.
#define foreach(var, container) for (typeof((container).begin()) var = (container).begin(); var != (container).end(); ++var)
لا يوجد نوع في C++ ...كيف يتم تجميع هذا بالنسبة لك؟(من المؤكد أنها ليست محمولة)
لقد نفذت بلدي foreach_value
على أساس Boost
foreach
شفرة:
#include <boost/preprocessor/cat.hpp>
#define MUNZEKONZA_FOREACH_IN_MAP_ID(x) BOOST_PP_CAT(x, __LINE__)
namespace munzekonza {
namespace foreach_in_map_private {
inline bool set_false(bool& b) {
b = false;
return false;
}
}
}
#define MUNZEKONZA_FOREACH_VALUE(value, map) \
for(auto MUNZEKONZA_FOREACH_IN_MAP_ID(_foreach_in_map_it) = map.begin(); \
MUNZEKONZA_FOREACH_IN_MAP_ID(_foreach_in_map_it) != map.end();) \
for(bool MUNZEKONZA_FOREACH_IN_MAP_ID(_foreach_in_map_continue) = true; \
MUNZEKONZA_FOREACH_IN_MAP_ID(_foreach_in_map_continue) && \
MUNZEKONZA_FOREACH_IN_MAP_ID(_foreach_in_map_it) != map.end(); \
(MUNZEKONZA_FOREACH_IN_MAP_ID(_foreach_in_map_continue)) ? \
((void)++MUNZEKONZA_FOREACH_IN_MAP_ID(_foreach_in_map_it)) : \
(void)0) \
if( munzekonza::foreach_in_map_private::set_false( \
MUNZEKONZA_FOREACH_IN_MAP_ID(_foreach_in_map_continue))) {} else \
for( value = MUNZEKONZA_FOREACH_IN_MAP_ID(_foreach_in_map_it)->second; \
!MUNZEKONZA_FOREACH_IN_MAP_ID(_foreach_in_map_continue); \
MUNZEKONZA_FOREACH_IN_MAP_ID(_foreach_in_map_continue) = true)
على سبيل المثال، يمكنك استخدامه في التعليمات البرمجية الخاصة بك مثل هذا:
#define MUNZEKONZA_FOREACH_VALUE foreach_value
std::map<int, std::string> mymap;
// populate the map ...
foreach_value( const std::string& value, mymap ) {
// do something with value
}
// change value
foreach_value( std::string& value, mymap ) {
value = "hey";
}
#define zforeach(var, container) for(auto var = (container).begin(); var != (container).end(); ++var)
لا يوجد typeof() لذا يمكنك استخدام هذا:
decltype((container).begin()) var
decltype(container)::iterator var