জিসিসির ## __ VA_ARGS__ কৌশলটির স্ট্যান্ডার্ড বিকল্প?


151

C99 এ ভেরিয়েডিক ম্যাক্রোগুলির জন্য খালি আরগগুলির সাথে একটি সুপরিচিত সমস্যা রয়েছে।

উদাহরণ:

#define FOO(...)       printf(__VA_ARGS__)
#define BAR(fmt, ...)  printf(fmt, __VA_ARGS__)

FOO("this works fine");
BAR("this breaks!");

BAR()উপরের ব্যবহারটি সত্যই C99 স্ট্যান্ডার্ড অনুযায়ী ভুল, যেহেতু এটি প্রসারিত হবে:

printf("this breaks!",);

পেছনের কমাটি নোট করুন - কার্যক্ষম নয় work

কিছু সংকলক (উদাহরণস্বরূপ: ভিজ্যুয়াল স্টুডিও 2010) আপনার জন্য চুপচাপ সেই পেছনের কমাটি থেকে মুক্তি পাবে। অন্যান্য সংকলক (যেমন: জিসিসি) ##এর সামনে রাখার পক্ষে সমর্থন করে __VA_ARGS__:

#define BAR(fmt, ...)  printf(fmt, ##__VA_ARGS__)

তবে এই আচরণটি পাওয়ার জন্য কি কোনও মান-মেনে চলার উপায় আছে? সম্ভবত একাধিক ম্যাক্রো ব্যবহার করছেন?

এখনই, ##সংস্করণটি মোটামুটি সু-সমর্থিত বলে মনে হচ্ছে (কমপক্ষে আমার প্ল্যাটফর্মগুলিতে) তবে আমি সত্যিই বরং মান-সম্মতিযুক্ত সমাধানটি ব্যবহার করব।

প্রাক-উদ্দীপক: আমি জানি আমি কেবল একটি ছোট ফাংশন লিখতে পারি। আমি ম্যাক্রো ব্যবহার করে এটি করার চেষ্টা করছি।

সম্পাদনা করুন : কেন আমি বার () ব্যবহার করতে চাই তার একটি উদাহরণ এখানে (সহজ হলেও) রয়েছে:

#define BAR(fmt, ...)  printf(fmt "\n", ##__VA_ARGS__)

BAR("here is a log message");
BAR("here is a log message with a param: %d", 42);

এটি স্বয়ংক্রিয়ভাবে আমার বার () লগিং স্টেটমেন্টগুলিতে একটি নতুন লাইন যুক্ত করে, ধরে নিই fmtযে সবসময় একটি ডাবল-কোটেড সি-স্ট্রিং থাকে। এটি পৃথক প্রিন্টফ () হিসাবে নতুন লাইনটি মুদ্রণ করে না, যা লগিং লাইন-বাফার করে এবং একাধিক উত্স থেকে অবিচ্ছিন্নভাবে আসে তা সুবিধাজনক।


3
প্রথম স্থানে BARপরিবর্তে কেন ব্যবহার করবেন FOO?
GManNickG

@ জিএমান: আমি শেষে একটি উদাহরণ যুক্ত করেছি
jwd

5
@ জিএমান: শেষ বাক্যটি পড়ুন (:
jwd


2
@Zwol ডাব্লুজি 14 এ জমা দেওয়া সর্বশেষ সংস্করণটি এর মতো দেখাচ্ছে , যা মূলশব্দের উপর ভিত্তি করে একটি নতুন সিনট্যাক্স ব্যবহার করে __VA_OPT__। এই ইতিমধ্যে হয়েছে "গৃহীত" করেছে C দ্বারা ++ তাই আমি আশা সি মামলা অনুসরণ করা হবে। (জানেন না যে এর অর্থ এটি দ্রুত সি ++ 17 তে ট্র্যাক হয়েছিল কিনা বা এটি যদিও সি ++ 20 এর জন্য সেট করা হয়েছে)
লুশেনকো

উত্তর:


66

এই প্রশ্নের রিচার্ড হ্যানসেনের উত্তরে,##__VA_ARGS__ বর্ণিত হিসাবে, আপনি যদি আপনার বৈকল্পিক ম্যাক্রোটিতে যে পরিমাণ যুক্তি দিতে পারেন তার উপর কিছু হার্ডকোডযুক্ত উচ্চতর সীমাটি গ্রহণ করতে ইচ্ছুক হন, তবে জিসিসির এক্সটেনশনের ব্যবহার এড়ানো সম্ভব । আপনি যদি এই জাতীয় কোনও সীমা না রাখতে চান তবে আমার জ্ঞানের সেরাটি কেবলমাত্র C99- নির্দিষ্ট প্রিপ্রসেসর বৈশিষ্ট্যগুলি ব্যবহার করে সম্ভব নয়; আপনার অবশ্যই ভাষাতে কিছু এক্সটেনশন ব্যবহার করতে হবে। ঝনঝন এবং আইসিসি এই জিসিসি এক্সটেনশন গ্রহণ করেছে, তবে এমএসভিসি তা দেয়নি।

2001 সালে আমি মানকীকরণের জন্য জিসিসি এক্সটেনশন লিখেছিলাম (এবং সম্পর্কিত এক্সটেনশন যা আপনাকে __VA_ARGS__বাকী-প্যারামিটার বাদে অন্য কোনও নাম ব্যবহার করতে দেয় ) ডকুমেন্ট এন 976 এ , তবে কমিটির কাছ থেকে এর কোনও সাড়া পাওয়া যায়নি; কেউ এটা পড়ে কিনা জানি না। ২০১ In সালে এটি আবার N2023 এ প্রস্তাব করা হয়েছিল , এবং আমি যে কাউকে জানি যে প্রস্তাবটি কীভাবে আমাদের মন্তব্যগুলিতে জানাতে চলেছে উত্সাহিত করি।


2
ওয়েবে এবং এখানে উত্তরগুলির অভাবের সমাধান খুঁজে পেতে আমার অক্ষমতা বিচার করে আমি অনুমান করি আপনি ঠিক বলেছেন):
jwd

2
আপনি উল্লেখ করছেন N976 হয়? আমি বাকি অনুসন্ধান সি ওয়ার্কিং গ্রুপ এর কাগজপত্র একটি প্রতিক্রিয়ার জন্য কিন্তু এক না পাওয়া যায় নি। এটি পরবর্তী বৈঠকের এজেন্ডায়ও ছিল না । এই বিষয়টিতে কেবলমাত্র অন্যরকম আঘাত হ'ল নরওয়ের মন্তব্য # 4 N868সি -৯৯ অনুমোদিত হওয়ার আগে থেকে (আবার কোনও ফলো-আপ আলোচনা ছাড়াই) নয়।
রিচার্ড হ্যানসেন

4
হ্যাঁ, বিশেষত এর দ্বিতীয়ার্ধে। আলোচনা হতে পারে comp.std.cতবে আমি এখনই গুগল গ্রুপগুলিতে কোনও সন্ধান করতে অক্ষম ছিলাম; এটি অবশ্যই প্রকৃত কমিটির কাছ থেকে কোনও দৃষ্টি আকর্ষণ করেনি (বা যদি তা করে থাকে তবে কেউই আমাকে এ সম্পর্কে কখনও বলেনি)।
zwol

1
আমি ভয় করি যে আমার কাছে প্রমাণ নেই, আমিও ঠিক একজনকে ভাবতে চেষ্টা করার মতো সঠিক ব্যক্তি নই। আমি জিসিসির অর্ধেক প্রিপ্রোসেসর লিখেছিলাম, তবে এটি দশ বছর আগে ছিল এবং আমি তবুও নীচের তর্ক-গণনা কৌশলটি কখনও ভাবিনি।
zwol

6
এই এক্সটেনশনটি ক্ল্যাং এবং ইন্টেল আইসিসি সংকলকগুলির পাশাপাশি জিসিসির সাথে কাজ করে।
এসাইক্লিক

112

একটি যুক্তি গণনা করার কৌশল রয়েছে যা আপনি ব্যবহার করতে পারেন।

BAR()Jwd এর প্রশ্নের দ্বিতীয় উদাহরণটি প্রয়োগ করার জন্য এখানে একটি মান- সম্মতিযুক্ত উপায় :

#include <stdio.h>

#define BAR(...) printf(FIRST(__VA_ARGS__) "\n" REST(__VA_ARGS__))

/* expands to the first argument */
#define FIRST(...) FIRST_HELPER(__VA_ARGS__, throwaway)
#define FIRST_HELPER(first, ...) first

/*
 * if there's only one argument, expands to nothing.  if there is more
 * than one argument, expands to a comma followed by everything but
 * the first argument.  only supports up to 9 arguments but can be
 * trivially expanded.
 */
#define REST(...) REST_HELPER(NUM(__VA_ARGS__), __VA_ARGS__)
#define REST_HELPER(qty, ...) REST_HELPER2(qty, __VA_ARGS__)
#define REST_HELPER2(qty, ...) REST_HELPER_##qty(__VA_ARGS__)
#define REST_HELPER_ONE(first)
#define REST_HELPER_TWOORMORE(first, ...) , __VA_ARGS__
#define NUM(...) \
    SELECT_10TH(__VA_ARGS__, TWOORMORE, TWOORMORE, TWOORMORE, TWOORMORE,\
                TWOORMORE, TWOORMORE, TWOORMORE, TWOORMORE, ONE, throwaway)
#define SELECT_10TH(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, ...) a10

int
main(int argc, char *argv[])
{
    BAR("first test");
    BAR("second test: %s", "a string");
    return 0;
}

এই একই কৌশলটি ব্যবহৃত হয়:

ব্যাখ্যা

কৌশলটি হ'ল __VA_ARGS__প্রথম যুক্তি এবং বাকীগুলি (যদি থাকে তবে) আলাদা করা। এটি প্রথম আর্গুমেন্টের পরে তবে দ্বিতীয়টির আগে (উপস্থিত থাকলে) স্টাফগুলি সন্নিবেশ করা সম্ভব করে তোলে।

FIRST()

এই ম্যাক্রোটি কেবল প্রথম যুক্তিতে প্রসারিত করে বাকী অংশগুলি অস্বীকার করে।

বাস্তবায়ন সোজা। throwawayযুক্তি নিশ্চিত করে যে FIRST_HELPER()দুটি আর্গুমেন্ট পায়, যা প্রয়োজন বোধ করা হয় কারণ ...অন্তত একটি এ চাহিদা। একটি যুক্তি সহ, এটি নিম্নলিখিত হিসাবে প্রসারিত:

  1. FIRST(firstarg)
  2. FIRST_HELPER(firstarg, throwaway)
  3. firstarg

দুই বা ততোধিকের সাথে এটি নীচে প্রসারিত হয়:

  1. FIRST(firstarg, secondarg, thirdarg)
  2. FIRST_HELPER(firstarg, secondarg, thirdarg, throwaway)
  3. firstarg

REST()

এই ম্যাক্রোটি প্রথম আর্গুমেন্ট ব্যতীত সমস্ত ক্ষেত্রে প্রসারিত হয় (একাধিক যুক্তি থাকলে প্রথম যুক্তির পরে কমা সহ)।

এই ম্যাক্রোর প্রয়োগ আরও জটিল। সাধারণ কৌশল হ'ল আর্গুমেন্টগুলির সংখ্যা গণনা করা (এক বা একাধিক) এবং তারপরে প্রসারিত হয় REST_HELPER_ONE()(যদি কেবল একটি যুক্তি দেওয়া হয়) বা REST_HELPER_TWOORMORE()(যদি দুটি বা ততোধিক যুক্তি দেওয়া হয়)। REST_HELPER_ONE()কেবল কিছুতেই প্রসারিত হয় - প্রথমটির পরে কোনও যুক্তি নেই, সুতরাং বাকী যুক্তিগুলি খালি সেট। REST_HELPER_TWOORMORE()এটিও সোজা - এটি প্রথম যুক্তি ব্যতীত সমস্ত কিছু অনুসরণ করে কমাতে প্রসারিত হয়।

যুক্তিগুলি NUM()ম্যাক্রো ব্যবহার করে গণনা করা হয় । এই ম্যাক্রো প্রসারিত হয় ONEযদি কেবল একটি যুক্তি দেওয়া হয়, TWOORMOREযদি দুটি থেকে নয়টি আর্গুমেন্ট দেওয়া হয় এবং 10 বা ততোধিক যুক্তি দেওয়া হয় তবে ব্রেক হয় (কারণ এটি 10 ​​তর্কের দিকে প্রসারিত হয়)।

NUM()ম্যাক্রো ব্যবহার SELECT_10TH()আর্গুমেন্টের সংখ্যা নির্ধারণ করতে ম্যাক্রো। এর নামটি যেমন বোঝা যাচ্ছে, SELECT_10TH()কেবল এটি তার দশম আর্গুমেন্টে প্রসারিত। উপবৃত্তির কারণে, SELECT_10TH()কমপক্ষে 11 টি আর্গুমেন্ট পাস করতে হবে (স্ট্যান্ডার্ডটি বলে যে উপবৃত্তির জন্য কমপক্ষে একটি যুক্তি থাকতে হবে)। এই কারণেই সর্বশেষ যুক্তি হিসাবে NUM()পাস throwawayহয় (এটি ব্যতীত, একটি যুক্তি পাস করার NUM()ফলে কেবলমাত্র 10 টি আর্গুমেন্টই পাস হয় SELECT_10TH(), যা মান লঙ্ঘন করবে)।

পারেন নির্বাচন REST_HELPER_ONE()বা REST_HELPER_TWOORMORE()concatenating দ্বারা সম্পন্ন করা হয় REST_HELPER_প্রসারের NUM(__VA_ARGS__)মধ্যে REST_HELPER2()। নোট করুন যে এর উদ্দেশ্যটি REST_HELPER()নিশ্চিত করা যে NUM(__VA_ARGS__)এটির সাথে সম্মিলিত হওয়ার আগে এটি পুরোপুরি প্রসারিত হয়েছে REST_HELPER_

একটি যুক্তির সাথে সম্প্রসারণ নিম্নরূপ:

  1. REST(firstarg)
  2. REST_HELPER(NUM(firstarg), firstarg)
  3. REST_HELPER2(SELECT_10TH(firstarg, TWOORMORE, TWOORMORE, TWOORMORE, TWOORMORE, TWOORMORE, TWOORMORE, TWOORMORE, TWOORMORE, ONE, throwaway), firstarg)
  4. REST_HELPER2(ONE, firstarg)
  5. REST_HELPER_ONE(firstarg)
  6. (খালি)

দুই বা ততোধিক আর্গুমেন্টের সাহায্যে সম্প্রসারণ নীচে চলেছে:

  1. REST(firstarg, secondarg, thirdarg)
  2. REST_HELPER(NUM(firstarg, secondarg, thirdarg), firstarg, secondarg, thirdarg)
  3. REST_HELPER2(SELECT_10TH(firstarg, secondarg, thirdarg, TWOORMORE, TWOORMORE, TWOORMORE, TWOORMORE, TWOORMORE, TWOORMORE, TWOORMORE, TWOORMORE, ONE, throwaway), firstarg, secondarg, thirdarg)
  4. REST_HELPER2(TWOORMORE, firstarg, secondarg, thirdarg)
  5. REST_HELPER_TWOORMORE(firstarg, secondarg, thirdarg)
  6. , secondarg, thirdarg

1
দ্রষ্টব্য যে আপনি 10 বা ততোধিক যুক্তি দিয়ে বারকে কল করলে এটি ব্যর্থ হবে এবং আরও বেশি যুক্তি প্রসারিত করা তুলনামূলক সহজ হলেও এটি যে আর্গুমেন্টগুলি মোকাবেলা করতে পারে তার উপর এটি সর্বদা উপরের আবদ্ধ থাকবে
ক্রিস ডড

2
@ ক্রিসডডড: সঠিক। দুর্ভাগ্যক্রমে, সংকলক-নির্দিষ্ট এক্সটেনশনের উপর নির্ভর না করে আর্গুমেন্টের সংখ্যা সীমাবদ্ধতা এড়ানোর উপায় বলে মনে হয় না। এছাড়াও, খুব বেশি আর্গুমেন্ট থাকলে নির্ভরযোগ্যভাবে পরীক্ষা করার কোনও উপায় সম্পর্কে আমি অসচেতন (যাতে কোনও অজানা ব্যর্থতার চেয়ে দরকারী সংকলক ত্রুটি বার্তাটি মুদ্রণ করা যায়)।
রিচার্ড হানসেন

17

কোনও সাধারণ সমাধান নয়, তবে প্রিন্টফের ক্ষেত্রে আপনি একটি নতুন লাইন যুক্ত করতে পারেন:

#define BAR_HELPER(fmt, ...) printf(fmt "\n%s", __VA_ARGS__)
#define BAR(...) BAR_HELPER(__VA_ARGS__, "")

আমি বিশ্বাস করি এটি কোনও অতিরিক্ত আরগগুলি উপেক্ষা করে যা বিন্যাসের স্ট্রিংয়ে উল্লেখ করা হয় না। সুতরাং আপনি সম্ভবত এড়িয়ে যেতে পারে:

#define BAR_HELPER(fmt, ...) printf(fmt "\n", __VA_ARGS__)
#define BAR(...) BAR_HELPER(__VA_ARGS__, 0)

আমি বিশ্বাস করতে পারি না যে এটি করার কোনও স্ট্যান্ডার্ড উপায় ছাড়াই সি 99 অনুমোদিত হয়েছিল। আফ্রিএফটি সমস্যাটি C ++ 11 এও বিদ্যমান।


এই অতিরিক্ত 0 টির সাথে সমস্যাটি হ'ল এটি ভারড ফাংশনটি কল করলে এটি কোডে আসলে শেষ হয়। রিচার্ড হ্যানসেন দ্বারা সরবরাহিত সমাধানের জন্য পরীক্ষা করুন
পাভেল পি

@ পাভেল দ্বিতীয় উদাহরণটি সম্পর্কে সঠিক, তবে প্রথমটি দুর্দান্ত কাজ করে। +1 টি।
kirbyfan64sos

11

বুস্ট.প্রিপ্রসেসরের মতো কিছু ব্যবহার করে এই নির্দিষ্ট কেসটি হ্যান্ডেল করার একটি উপায় রয়েছে । আপনি যুক্তি তালিকার আকারটি পরীক্ষা করতে BOOST_PP_VARIADIC_SIZE ব্যবহার করতে পারেন এবং তারপরে শর্তসাপেক্ষে অন্য ম্যাক্রোতে প্রসারিত করতে পারেন। এর একটি ঘাটতি হ'ল এটি 0 এবং 1 টি আর্গুমেন্টের মধ্যে পার্থক্য করতে পারে না এবং আপনি নিম্নলিখিতগুলি বিবেচনা করার পরে এর কারণ স্পষ্ট হয়ে ওঠে:

BOOST_PP_VARIADIC_SIZE()      // expands to 1
BOOST_PP_VARIADIC_SIZE(,)     // expands to 2
BOOST_PP_VARIADIC_SIZE(,,)    // expands to 3
BOOST_PP_VARIADIC_SIZE(a)     // expands to 1
BOOST_PP_VARIADIC_SIZE(a,)    // expands to 2
BOOST_PP_VARIADIC_SIZE(,b)    // expands to 2
BOOST_PP_VARIADIC_SIZE(a,b)   // expands to 2
BOOST_PP_VARIADIC_SIZE(a, ,c) // expands to 3

খালি ম্যাক্রো আর্গুমেন্ট তালিকায় আসলে একটি যুক্তি থাকে যা খালি হয়ে যায়।

এই ক্ষেত্রে, আমরা ভাগ্যবান যেহেতু আপনার পছন্দসই ম্যাক্রোর সর্বদা কমপক্ষে 1 টি যুক্তি থাকে, আমরা এটি দুটি "ওভারলোড" ম্যাক্রো হিসাবে প্রয়োগ করতে পারি:

#define BAR_0(fmt) printf(fmt "\n")
#define BAR_1(fmt, ...) printf(fmt "\n", __VA_ARGS__)

এবং তারপরে তাদের মধ্যে স্যুইচ করার জন্য আরও একটি ম্যাক্রো যেমন:

#define BAR(...) \
    BOOST_PP_CAT(BAR_, BOOST_PP_GREATER(
        BOOST_PP_VARIADIC_SIZE(__VA_ARGS__), 1))(__VA_ARGS__) \
    /**/

অথবা

#define BAR(...) BOOST_PP_IIF( \
    BOOST_PP_GREATER(BOOST_PP_VARIADIC_SIZE(__VA_ARGS__), 1), \
        BAR_1, BAR_0)(__VA_ARGS__) \
    /**/

যাকে আপনি আরও পঠনযোগ্য মনে করেন (আর্গুমেন্টের সংখ্যায় ম্যাক্রোকে ওভারলোড করার জন্য এটি আপনাকে সাধারণ ফর্ম দেয়ায় আমি প্রথমটিকে পছন্দ করি)।

পরিবর্তনশীল আর্গুমেন্টের তালিকাটি অ্যাক্সেস এবং পরিবর্তন করে একক ম্যাক্রো দিয়ে এটি করাও সম্ভব, তবে এটি কম পাঠযোগ্য, এবং এই সমস্যার সাথে খুব নির্দিষ্ট:

#define BAR(...) printf( \
    BOOST_PP_VARIADIC_ELEM(0, __VA_ARGS__) "\n" \
    BOOST_PP_COMMA_IF( \
        BOOST_PP_GREATER(BOOST_PP_VARIADIC_SIZE(__VA_ARGS__), 1)) \
    BOOST_PP_ARRAY_ENUM(BOOST_PP_ARRAY_POP_FRONT( \
        BOOST_PP_VARIADIC_TO_ARRAY(__VA_ARGS__)))) \
    /**/

এছাড়াও, কেন কোনও BOOST_PP_ARRAY_ENUM_TRAILING নেই? এটি এই সমাধানটিকে অনেক কম ভয়ঙ্কর করে তুলবে।

সম্পাদনা: ঠিক আছে, এখানে একটি BOOST_PP_ARRAY_ENUM_TRAILING, এবং এটি ব্যবহার করা একটি সংস্করণ রয়েছে (এটি এখন আমার প্রিয় সমাধান):

#define BOOST_PP_ARRAY_ENUM_TRAILING(array) \
    BOOST_PP_COMMA_IF(BOOST_PP_ARRAY_SIZE(array)) BOOST_PP_ARRAY_ENUM(array) \
    /**/

#define BAR(...) printf( \
    BOOST_PP_VARIADIC_ELEM(0, __VA_ARGS__) "\n" \
    BOOST_PP_ARRAY_ENUM_TRAILING(BOOST_PP_ARRAY_POP_FRONT( \
        BOOST_PP_VARIADIC_TO_ARRAY(__VA_ARGS__)))) \
    /**/

1
বুস্ট.প্রিপ্রসেসর, +1 সম্পর্কে জানতে পেরে ভাল লাগল। দ্রষ্টব্য যে BOOST_PP_VARIADIC_SIZE()আমি আমার উত্তরে নথিভুক্ত একই যুক্তি গণনা কৌশল ব্যবহার করি এবং এর একই সীমাবদ্ধতা রয়েছে (যদি আপনি একটি নির্দিষ্ট সংখ্যক তর্ক যুক্তি দিয়ে পাস করেন তবে এটি ভেঙে যাবে)।
রিচার্ড হ্যানসেন

1
হ্যাঁ, আমি দেখেছি যে আপনার পদ্ধতির বুস্ট একই ব্যবহার করেছিলেন, তবে বুস্ট সলিউশনটি খুব ভালভাবে বজায় থাকে এবং আরও পরিশীলিত ম্যাক্রোগুলি বিকাশ করার সময় ব্যবহারের জন্য আরও অনেক দরকারী উপসর্গ রয়েছে। পুনরাবৃত্তির স্টাফগুলি বিশেষত দুর্দান্ত (এবং সর্বশেষ পন্থায় যেগুলি BOOST_PP_ARRAY_ENUM ব্যবহার করে সেগুলির নেপথ্যে ব্যবহৃত হয়)।
DRayX

1
একটি বুস্ট উত্তর যা আসলে সি ট্যাগের জন্য প্রযোজ্য ! হুররে!
জাস্টিন

6

ডিবাগ প্রিন্টিংয়ের জন্য আমি খুব সাধারণ ম্যাক্রো ব্যবহার করছি:

#define __DBG_INT(fmt, ...) printf(fmt "%s", __VA_ARGS__);
#define DBG(...) __DBG_INT(__VA_ARGS__, "\n")

int main() {
        DBG("No warning here");
        DBG("and we can add as many arguments as needed. %s", "nice!");
        return 0;
}

ডিবিজিতে কত আর্গুমেন্ট পাস হয়েছে তা বিবেচনা করেই কোনও সি 99 সতর্কতা নেই।

কৌশলটি __DBG_INTএকটি ডামি পরম যুক্ত করছে তাই ...সর্বদা কমপক্ষে একটি যুক্তি থাকবে এবং সি 99 সন্তুষ্ট থাকবে।


5

আমি সম্প্রতি একটি অনুরূপ সমস্যার মধ্যে দৌড়েছি, এবং আমি বিশ্বাস করি যে এর কোনও সমাধান আছে।

মূল ধারণাটি হ'ল NUM_ARGSএকটি বৈকল্পিক ম্যাক্রো প্রদত্ত যুক্তিগুলির সংখ্যা গণনা করার জন্য ম্যাক্রো লেখার একটি উপায় রয়েছে । আপনি NUM_ARGSনির্মাণের জন্য একটি প্রকরণ ব্যবহার করতে পারেন NUM_ARGS_CEILING2, যা আপনাকে বলতে পারে যে কোনও ভেরিয়েডিক ম্যাক্রোকে 1 টি আর্গুমেন্ট বা 2-বা আরও বেশি যুক্তি দেওয়া হয় কিনা। তারপরে আপনি আপনার Barম্যাক্রোটি লিখতে পারেন যাতে এটি ব্যবহার করে NUM_ARGS_CEILING2এবং CONCATদুটি সহায়ক সহায়ক ম্যাক্রোগুলির মধ্যে একটিতে তার যুক্তিগুলি প্রেরণ করতে পারে: একটি যা সঠিকভাবে 1 টি যুক্তি প্রত্যাশা করে এবং অন্যটি 1 এর চেয়েও বেশি চলক আর্গুমেন্টের প্রত্যাশা করে।

এখানে একটি উদাহরণ যেখানে আমি এই কৌতুক ব্যবহার ম্যাক্রো লিখতে এর UNIMPLEMENTED, যা খুবই অনুরূপ BAR:

ধাপ 1:

/** 
 * A variadic macro which counts the number of arguments which it is
 * passed. Or, more precisely, it counts the number of commas which it is
 * passed, plus one.
 *
 * Danger: It can't count higher than 20. If it's given 0 arguments, then it
 * will evaluate to 1, rather than to 0.
 */

#define NUM_ARGS(...)                                                   \
    NUM_ARGS_COUNTER(__VA_ARGS__, 20, 19, 18, 17, 16, 15, 14, 13,       \
                     12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1)    

#define NUM_ARGS_COUNTER(a1, a2, a3, a4, a5, a6, a7,        \
                         a8, a9, a10, a11, a12, a13,        \
                         a14, a15, a16, a17, a18, a19, a20, \
                         N, ...)                            \
    N

পদক্ষেপ 1.5:

/*
 * A variant of NUM_ARGS that evaluates to 1 if given 1 or 0 args, or
 * evaluates to 2 if given more than 1 arg. Behavior is nasty and undefined if
 * it's given more than 20 args.
 */

#define NUM_ARGS_CEIL2(...)                                           \
    NUM_ARGS_COUNTER(__VA_ARGS__, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, \
                     2, 2, 2, 2, 2, 2, 2, 1)

ধাপ ২:

#define _UNIMPLEMENTED1(msg)                                        \
    log("My creator has forsaken me. %s:%s:%d." msg, __FILE__,      \
        __func__, __LINE__)

#define _UNIMPLEMENTED2(msg, ...)                                   \
    log("My creator has forsaken me. %s:%s:%d." msg, __FILE__,      \
        __func__, __LINE__, __VA_ARGS__)

ধাপ 3:

#define UNIMPLEMENTED(...)                                              \
    CONCAT(_UNIMPLEMENTED, NUM_ARGS_CEIL2(__VA_ARGS__))(__VA_ARGS__)

যেখানে কনক্যাটটি যথাযথভাবে প্রয়োগ করা হয়। দ্রুত ইঙ্গিত হিসাবে, যদি উপরেরটি বিভ্রান্ত মনে হয়: কনক্যাটটির লক্ষ্যটি অন্য ম্যাক্রো "কল" এ প্রসারিত করা।

মনে রাখবেন যে NUM_ARGS নিজেই ব্যবহৃত হয়নি। আমি এখানে এটি প্রাথমিক কৌশলটি বর্ণনা করার জন্য অন্তর্ভুক্ত করেছি। এর সুন্দর চিকিত্সার জন্য জেনস গুষ্টডের পি 99 ব্লগটি দেখুন ।

দুটি নোট:

  • NUM_ARGS এটি পরিচালনা করে এমন আর্গুমেন্টের সংখ্যার মধ্যে সীমাবদ্ধ। খনিটি কেবল 20 টি পর্যন্ত পরিচালনা করতে পারে, যদিও সংখ্যাটি সম্পূর্ণ নির্বিচারে।

  • NUM_ARGS, যেমন দেখানো হয়েছে, তেমন একটি সমস্যা রয়েছে যা 0 টি আর্গুমেন্ট দেওয়ার পরে এটি 1 প্রদান করে। এর সংক্ষিপ্তসারটি হ'ল NUM_ARGS প্রযুক্তিগতভাবে [কমা + 1] গণনা করছে, আরগস নয়। এই বিশেষ ক্ষেত্রে, এটি আসলে আমাদের সুবিধার জন্য কাজ করে। _ UNIMPLEMENTED1 খালি টোকেনটি ঠিকঠাকভাবে পরিচালনা করবে এবং এটি _ UNIMPLEMENTED0 লিখতে থেকে আমাদের বাঁচায়। গুস্ট্টেরও এর জন্য একদম কাজ রয়েছে, যদিও আমি এটি ব্যবহার করি নি এবং আমি নিশ্চিত না যে আমরা এখানে যা করছি তার জন্য এটি কাজ করবে কিনা।


যুক্তি গণনার কৌশলটি সামনে আনার জন্য +1, অনুসরণ করা সত্যিই কঠিন বলে -1
রিচার্ড হ্যানসেন

আপনি যে মন্তব্যগুলি যুক্ত করেছেন সেগুলি ছিল উন্নতি, তবে এখনও অনেকগুলি সমস্যা রয়েছে: ১. আপনি আলোচনা করেন এবং সংজ্ঞা দেন NUM_ARGSতবে এটি ব্যবহার করবেন না। ২) এর উদ্দেশ্য কী UNIMPLEMENTED? ৩. আপনি কখনই প্রশ্নের সমস্যার সমাধান করতে পারবেন না। ৪. একবারে এক ধাপে সম্প্রসারণের মধ্য দিয়ে হাঁটলে এটি কীভাবে কাজ করে তা চিত্রিত করে এবং প্রতিটি সহায়ক ম্যাক্রোর ভূমিকা ব্যাখ্যা করে। ৫. যুক্তি নিয়ে আলোচনা করা বিভ্রান্তিকর; ওপি মান সম্মতি সম্পর্কে জিজ্ঞাসা করছিল, এবং 0 টি আর্গুমেন্ট নিষিদ্ধ (C99 6.10.3p4)। 6. 1.5 পদক্ষেপ? দ্বিতীয় পদক্ষেপ কেন? ". "পদক্ষেপগুলি" ক্রমানুসারে ঘটে যাওয়া ক্রিয়াকে বোঝায়; এটা ঠিক কোড।
রিচার্ড হ্যানসেন

৮. আপনি পুরো ব্লগে লিঙ্ক করেছেন, সম্পর্কিত পোস্ট নয়। আপনি যে পোস্টটি উল্লেখ করছেন তা আমি খুঁজে পাইনি। 9. শেষ অনুচ্ছেদ বিশ্রী এই পদ্ধতি হল অস্পষ্ট; সে কারণেই এর আগে আর কেউ সঠিক সমাধান পোস্ট করেনি। এছাড়াও, যদি এটি কাজ করে এবং মানকে মেনে চলে, জ্যাকের উত্তরটি অবশ্যই ভুল হতে পারে। ১০. আপনার সংজ্ঞা দেওয়া উচিত CONCAT()- পাঠকরা কীভাবে এটি কাজ করে তা জানেন না।
রিচার্ড হ্যানসেন

(দয়া করে এই প্রতিক্রিয়াটিকে আক্রমণ হিসাবে ব্যাখ্যা করবেন না - আমি আপনার উত্তরটিকে উচ্চতর করতে চাইতাম তবে এটি বুঝতে সহজতর না হলে এটি করা স্বাচ্ছন্দ্য বোধ করি না you আপনি যদি নিজের উত্তরের স্পষ্টতা আরও উন্নত করতে পারেন তবে আমি আপনার আপভোট করুন এবং আমার মুছুন))
রিচার্ড হ্যানসেন

2
আমি কখনও এই পদ্ধতির কথা ভাবিনি, এবং আমি জিসিসির বর্তমান প্রিপ্রোসেসর প্রায় অর্ধেক লিখেছি! এটি বলেছিল, আমি এখনও বলছি যে "এই প্রভাবটি পাওয়ার কোনও স্ট্যান্ডার্ড উপায় নেই" কারণ আপনার এবং রিচার্ডের উভয় কৌশলই ম্যাক্রোর পক্ষে যুক্তির সংখ্যার উপর একটি উচ্চতর সীমা চাপিয়ে দেয়।
zwol

2

এটি আমি ব্যবহার করা সরলিকৃত সংস্করণ। এটি এখানে অন্যান্য উত্তরের দুর্দান্ত কৌশলগুলির উপর ভিত্তি করে তৈরি হয়েছে, তাদের কাছে অনেকগুলি প্রপস:

#define _SELECT(PREFIX,_5,_4,_3,_2,_1,SUFFIX,...) PREFIX ## _ ## SUFFIX

#define _BAR_1(fmt)      printf(fmt "\n")
#define _BAR_N(fmt, ...) printf(fmt "\n", __VA_ARGS__);
#define BAR(...) _SELECT(_BAR,__VA_ARGS__,N,N,N,N,1)(__VA_ARGS__)

int main(int argc, char *argv[]) {
    BAR("here is a log message");
    BAR("here is a log message with a param: %d", 42);
    return 0;
}

এটাই.

অন্যান্য সমাধানের মতো এটি ম্যাক্রোর আর্গুমেন্টের সংখ্যার মধ্যে সীমাবদ্ধ। আরও সমর্থন করতে আরও প্যারামিটার _SELECTএবং আরও Nযুক্তি যুক্ত করুন। যুক্তির নামগুলি গণনাভিত্তিক SUFFIXযুক্তিটি বিপরীত ক্রমে সরবরাহ করা হয়েছে এমন একটি অনুস্মারক হিসাবে পরিবেশন করতে (আপ পরিবর্তে) গণনা করে ।

এই সমাধানটি 0 টি আর্গুমেন্ট হিসাবে বিবেচনা করে। সুতরাং BAR()নামমাত্র "কাজ করে", কারণ এটি প্রসারিত হয় _SELECT(_BAR,,N,N,N,N,1)(), যা প্রসারিত হয় _BAR_1()(), যা প্রসারিত হয় printf("\n")

আপনি যদি চান তবে আপনি _SELECTবিভিন্ন সংখ্যক তর্ক যুক্তি ব্যবহার করে সৃজনশীল পেতে পারেন এবং বিভিন্ন ম্যাক্রো সরবরাহ করতে পারেন । উদাহরণস্বরূপ, এখানে আমাদের কাছে একটি এলওজি ম্যাক্রো রয়েছে যা ফর্ম্যাটের আগে একটি 'স্তর' আর্গুমেন্ট নেয়। যদি ফর্ম্যাটটি অনুপস্থিত থাকে তবে এটি "(কোনও বার্তা নেই)" লগ করে, যদি কেবল 1 টি আর্গুমেন্ট থাকে তবে এটি "% s" এর মাধ্যমে এটি লগ করবে, অন্যথায় এটি বাকী আর্গুমেন্টগুলির জন্য ফর্ম্যাট আর্গুমেন্টটিকে একটি প্রিন্টফ ফর্ম্যাট স্ট্রিং হিসাবে বিবেচনা করবে।

#define _LOG_1(lvl)          printf("[%s] (no message)\n", #lvl)
#define _LOG_2(lvl,fmt)      printf("[%s] %s\n", #lvl, fmt)
#define _LOG_N(lvl,fmt, ...) printf("[%s] " fmt "\n", #lvl, __VA_ARGS__)
#define LOG(...) _SELECT(_LOG,__VA_ARGS__,N,N,N,2,1)(__VA_ARGS__)

int main(int argc, char *argv[]) {
    LOG(INFO);
    LOG(DEBUG, "here is a log message");
    LOG(WARN, "here is a log message with param: %d", 42);
    return 0;
}
/* outputs:
[INFO] (no message)
[DEBUG] here is a log message
[WARN] here is a log message with param: 42
*/

-প্যাডেন্টিকের সাথে সংকলিত হয়ে গেলে এটি এখনও একটি সতর্কবার্তা ট্রিগার করে।
PSkocik

1

আপনার অবস্থা (অন্তত 1 যুক্তি উপস্থিত, কখনও 0), আপনি নির্ধারণ করতে পারেন BARহিসাবে BAR(...)ব্যবহার জেনস Gustedt এর HAS_COMMA(...) একটি কমা সনাক্ত করতে এবং তারপর প্রাণবধ BAR0(Fmt)বা BAR1(Fmt,...)সেই অনুযায়ী।

এই:

#define HAS_COMMA(...) HAS_COMMA_16__(__VA_ARGS__, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0)
#define HAS_COMMA_16__(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, ...) _15
#define CAT_(X,Y) X##Y
#define CAT(X,Y) CAT_(X,Y)
#define BAR(.../*All*/) CAT(BAR,HAS_COMMA(__VA_ARGS__))(__VA_ARGS__)
#define BAR0(X) printf(X "\n")
#define BAR1(X,...) printf(X "\n",__VA_ARGS__)


#include <stdio.h>
int main()
{
    BAR("here is a log message");
    BAR("here is a log message with a param: %d", 42);
}

-pedanticএকটি সতর্কতা ছাড়া সংকলন ।


0

সি (জিসিসি) , 762 বাইট

#define EMPTYFIRST(x,...) A x (B)
#define A(x) x()
#define B() ,

#define EMPTY(...) C(EMPTYFIRST(__VA_ARGS__) SINGLE(__VA_ARGS__))
#define C(...) D(__VA_ARGS__)
#define D(x,...) __VA_ARGS__

#define SINGLE(...) E(__VA_ARGS__, B)
#define E(x,y,...) C(y(),)

#define NONEMPTY(...) F(EMPTY(__VA_ARGS__) D, B)
#define F(...) G(__VA_ARGS__)
#define G(x,y,...) y()

#define STRINGIFY(...) STRINGIFY2(__VA_ARGS__)
#define STRINGIFY2(...) #__VA_ARGS__

#define BAR(fmt, ...) printf(fmt "\n" NONEMPTY(__VA_ARGS__) __VA_ARGS__)

int main() {
    puts(STRINGIFY(NONEMPTY()));
    puts(STRINGIFY(NONEMPTY(1)));
    puts(STRINGIFY(NONEMPTY(,2)));
    puts(STRINGIFY(NONEMPTY(1,2)));

    BAR("here is a log message");
    BAR("here is a log message with a param: %d", 42);
}

এটি অনলাইন চেষ্টা করুন!

অনুমান:

  • কোনও আরগের মধ্যে কমা বা বন্ধনী নেই
  • কোনও আর্গ নেই A~ G(হার্ড_ক্লাইডের নাম পরিবর্তন করতে পারে)

no arg contain commaসীমাবদ্ধতা আরো কিছু পাস পর বহু চেক করে বাইপাস হতে পারে, কিন্তু no bracketএখনও সেখানে
l4m2

-2

স্ট্যান্ডার্ড সমাধান FOOপরিবর্তে ব্যবহার করা হয় BAR। যুক্তিটির পুনঃনির্মাণের কয়েকটি অদ্ভুত ঘটনা সম্ভবত এটি আপনার পক্ষে সম্ভব না (যদিও আমি বাজি ধরেছি যে কেউ __VA_ARGS__যুক্তিযুক্ত সংখ্যার ভিত্তিতে শর্তসাপেক্ষে বিচ্ছিন্ন ও পুনরায় সংশ্লেষ করতে চালাক হ্যাক নিয়ে আসতে পারে!) তবে সাধারণভাবে FOO"সাধারণত" ব্যবহার করে শুধু কাজ করে।


1
প্রশ্নটি ছিল "এই আচরণটি পাওয়ার কোনও মানদণ্ড রয়েছে কি?"
মার্শ রায়

2
এবং প্রশ্নটিতে এখন বয়সীদের জন্য এফইও ব্যবহার না করার যুক্তি যুক্ত করা হয়েছে।
পাভেল Šিমেরদা
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.