c - সি কিভাবে ফাংশন overloading অর্জন?




(10)

সি মধ্যে ফাংশন overloading অর্জন করার কোন উপায় আছে? আমি মত সহজ overloaded সহজ ফাংশন এ খুঁজছেন

foo (int a)  
foo (char b)  
foo (float c , int d)

আমি কোন সোজা এগিয়ে উপায় আছে মনে হয়; আমি যদি বিদ্যমান কোনো workarounds খুঁজছেন।


আমি আশা করি নিচের কোডটি আপনাকে ফাংশন ওভারলোডিং বুঝতে সহায়তা করবে

#include <stdio.h>
#include<stdarg.h>

int fun(int a, ...);
int main(int argc, char *argv[]){
   fun(1,10);
   fun(2,"cquestionbank");
   return 0;
}
int fun(int a, ...){
  va_list vl;
  va_start(vl,a);

  if(a==1)
      printf("%d",va_arg(vl,int));
   else
      printf("\n%s",va_arg(vl,char *));
}

অর্থে আপনি মানে - না, আপনি করতে পারেন না।

আপনি মত একটি va_arg ফাংশন ঘোষণা করতে পারেন

void my_func(char* format, ...);

, তবে আপনাকে প্রথম যুক্তিতে ভেরিয়েবলগুলির সংখ্যা এবং তাদের প্রকারের সম্পর্কে কিছু ধরণের তথ্য প্রেরণ করতে হবে - যেমন printf() করে।


আপনি কি সি ++ ব্যবহার করতে পারবেন না এবং এটি ছাড়া অন্য সমস্ত সি ++ বৈশিষ্ট্যগুলি ব্যবহার করবেন না?

যদি এখনও কোন কঠোর সি তারপর আমি পরিবর্তে ভেরিয়েডিক ফাংশন সুপারিশ করবে।


এখানে আমি পরিষ্কার এবং সর্বাধিক সংক্ষিপ্ত উদাহরন দেখিয়েছি সিটিতে ফাংশন ওভারলোডিং দেখানো হয়েছে:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int addi(int a, int b) {
    return a + b;
}

char *adds(char *a, char *b) {
    char *res = malloc(strlen(a) + strlen(b) + 1);
    strcpy(res, a);
    strcat(res, b);
    return res;
}

#define add(a, b) _Generic(a, int: addi, char*: adds)(a, b)

int main(void) {
    int a = 1, b = 2;
    printf("%d\n", add(a, b)); // 3

    char *c = "hello ", *d = "world";
    printf("%s\n", add(c, d)); // hello world

    return 0;
}

https://gist.github.com/barosl/e0af4a92b2b8cabd05a7


কিছু সম্ভাবনা আছে:

  1. printf শৈলী ফাংশন (একটি যুক্তি হিসাবে টাইপ করুন)
  2. opengl শৈলী ফাংশন (ফাংশন নাম টাইপ)
  3. c ++ এর সি সাবসেট (যদি আপনি একটি সি ++ কম্পাইলার ব্যবহার করতে পারেন)

নিম্নলিখিত পদ্ধতির একটি A2800276 এর অনুরূপ, কিন্তু কিছু C99 ম্যাক্রো জাদু যুক্ত:

// we need `size_t`
#include <stddef.h>

// argument types to accept
enum sum_arg_types { SUM_LONG, SUM_ULONG, SUM_DOUBLE };

// a structure to hold an argument
struct sum_arg
{
    enum sum_arg_types type;
    union
    {
        long as_long;
        unsigned long as_ulong;
        double as_double;
    } value;
};

// determine an array's size
#define count(ARRAY) ((sizeof (ARRAY))/(sizeof *(ARRAY)))

// this is how our function will be called
#define sum(...) _sum(count(sum_args(__VA_ARGS__)), sum_args(__VA_ARGS__))

// create an array of `struct sum_arg`
#define sum_args(...) ((struct sum_arg []){ __VA_ARGS__ })

// create initializers for the arguments
#define sum_long(VALUE) { SUM_LONG, { .as_long = (VALUE) } }
#define sum_ulong(VALUE) { SUM_ULONG, { .as_ulong = (VALUE) } }
#define sum_double(VALUE) { SUM_DOUBLE, { .as_double = (VALUE) } }

// our polymorphic function
long double _sum(size_t count, struct sum_arg * args)
{
    long double value = 0;

    for(size_t i = 0; i < count; ++i)
    {
        switch(args[i].type)
        {
            case SUM_LONG:
            value += args[i].value.as_long;
            break;

            case SUM_ULONG:
            value += args[i].value.as_ulong;
            break;

            case SUM_DOUBLE:
            value += args[i].value.as_double;
            break;
        }
    }

    return value;
}

// let's see if it works

#include <stdio.h>

int main()
{
    unsigned long foo = -1;
    long double value = sum(sum_long(42), sum_ulong(foo), sum_double(1e10));
    printf("%Le\n", value);
    return 0;
}

যেমনটি ইতিমধ্যে বলা হয়েছে, আপনি যে অর্থে বোঝাচ্ছেন তার উপর অতিরিক্ত লোড হচ্ছে সি দ্বারা সমর্থিত নয়। সমস্যার সমাধান করার জন্য সাধারণ নীতিমালাটি একটি ট্যাগযুক্ত ইউনিয়নকে গ্রহণ করে। এটি একটি struct প্যারামিটার দ্বারা প্রয়োগ করা হয়, যেখানে গঠন নিজেই কিছু ধরণের টাইপ সূচক, যেমন enum এবং বিভিন্ন ধরণের মানগুলির সমন্বয়ে গঠিত। উদাহরণ:

#include <stdio.h>

typedef enum {
    T_INT,
    T_FLOAT,
    T_CHAR,
} my_type;

typedef struct {
    my_type type;
    union {
        int a; 
        float b; 
        char c;
    } my_union;
} my_struct;

void set_overload (my_struct *whatever) 
{
    switch (whatever->type) 
    {
        case T_INT:
            whatever->my_union.a = 1;
            break;
        case T_FLOAT:
            whatever->my_union.b = 2.0;
            break;
        case T_CHAR:
            whatever->my_union.c = '3';
    }
}

void printf_overload (my_struct *whatever) {
    switch (whatever->type) 
    {
        case T_INT:
            printf("%d\n", whatever->my_union.a);
            break;
        case T_FLOAT:
            printf("%f\n", whatever->my_union.b);
            break;
        case T_CHAR:
            printf("%c\n", whatever->my_union.c);
            break;
    }

}

int main (int argc, char* argv[])
{
    my_struct s;

    s.type=T_INT;
    set_overload(&s);
    printf_overload(&s);

    s.type=T_FLOAT;
    set_overload(&s);
    printf_overload(&s);

    s.type=T_CHAR;
    set_overload(&s);
    printf_overload(&s); 
}

সাধারণত টাইপ সংযোজন বা নাম আগে নির্দেশ করা একটি wart। আপনি ম্যাক্রো দিয়ে দূরে পেতে পারেন কিছু উদাহরণ, তবে এটি আপনি যা করার চেষ্টা করছেন তার উপর নির্ভর করে। সি কোন polymorphism আছে, শুধুমাত্র জোরপূর্বক।

সাধারণ জেনেরিক অপারেশন ম্যাক্রো দিয়ে সম্পন্ন করা যেতে পারে:

#define max(x,y) ((x)>(y)?(x):(y))

আপনার কম্পাইলার typeof সমর্থন typeof , আরো জটিল অপারেশন ম্যাক্রো মধ্যে করা যেতে পারে। তারপরে আপনি একই অপারেশনটির বিভিন্ন ধরণের সমর্থন করতে foo (x) চিহ্নটি পেতে পারেন, তবে আপনি বিভিন্ন ওভারলোডগুলির মধ্যে আচরণ পরিবর্তন করতে পারবেন না। আপনি যদি ম্যাক্রোগুলির পরিবর্তে প্রকৃত ফাংশন চান তবে আপনি এই নামটিকে টাইপ করতে এবং এটি অ্যাক্সেস করতে দ্বিতীয় পেস্টিং ব্যবহার করতে সক্ষম হবেন (আমি চেষ্টা করে নিই)।


হ্যাঁ!

এই প্রশ্ন জিজ্ঞাসা করার সময় থেকে, স্ট্যান্ডার্ড সি (কোনও এক্সটেনশানস) কার্যকরী ওভারলোডিং (অপারেটরদের জন্য) সমর্থন করে না, ধন্যবাদ _Generic তে _Generic কীওয়ার্ড যোগ করার জন্য ধন্যবাদ। (সংস্করণ 4.9 থেকে GCC সমর্থিত)

(ওভারলোডিং প্রকৃতপক্ষে প্রশ্নটিতে প্রদর্শিত ফ্যাশনটিতে "অন্তর্নির্মিত" নয়, তবে এটি এমন কিছু কার্যকর করার জন্য সহজেই মৃত।)

_Generic এবং _Alignof মতো একই পরিবারের একটি কম্পাইল-টাইম অপারেটর। এটা স্ট্যান্ডার্ড বিভাগ 6.5.1.1 বর্ণিত হয়। এটি দুটি প্রধান প্যারামিটার গ্রহণ করে: একটি অভিব্যক্তি (যা রানটাইম এ মূল্যায়ন করা হবে না), এবং একটি টাইপ / এক্সপ্রেশন অ্যাসোসিয়েশনের তালিকা যা একটি switch ব্লকের মতো কিছু দেখায়। _Generic সামগ্রিক ধরনটি পান এবং তারপরে তার টাইপের তালিকাতে শেষ ফলাফল অভিব্যক্তি নির্বাচন করতে "সুইচ" করে:

_Generic(1, float: 2.0,
            char *: "2",
            int: 2,
            default: get_two_object());

উপরের অভিব্যক্তিটি মূল্যায়ন করে 2 - নিয়ন্ত্রণ অভিব্যক্তিটির ধরন int , তাই এটি মান হিসাবে int সহ যুক্ত অভিব্যক্তি পছন্দ করে। রানটাইম এ এই কিছুই অবশিষ্ট থাকে। ( default বিভাগটি ঐচ্ছিক: যদি আপনি এটি ছেড়ে যান এবং টাইপ মেলে না তবে এটি একটি সংকলন ত্রুটি সৃষ্টি করবে।)

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

#define cbrt(X) _Generic((X),                \
                         long double: cbrtl, \
                         default: cbrt,      \
                         float: cbrtf        \
                         )(X)

এই ম্যাক্রো একটি ওভারলোডেড cbrt অপারেশন প্রয়োগ করে, ম্যাক্রোর আর্গুমেন্টের ধরন প্রেরণ করে, যথাযথ বাস্তবায়ন ফাংশন নির্বাচন করে এবং তারপর সেই ফাংশনে মূল ম্যাক্রো আর্গুমেন্টটি পাস করে।

তাই আপনার আসল উদাহরণটি বাস্তবায়ন করতে আমরা এটি করতে পারি:

foo_int (int a)  
foo_char (char b)  
foo_float_int (float c , int d)

#define foo(_1, ...) _Generic((_1),                                  \
                              int: foo_int,                          \
                              char: foo_char,                        \
                              float: _Generic((FIRST(__VA_ARGS__,)), \
                                     int: foo_float_int))(_1, __VA_ARGS__)
#define FIRST(A, ...) A

এই ক্ষেত্রে আমরা একটি default: ব্যবহার করতে পারতাম default: তৃতীয় ক্ষেত্রে সহযোগিতা, কিন্তু এটি একাধিক আর্গুমেন্টের নীতিটি কীভাবে প্রসারিত করবেন তা প্রদর্শন করে না। শেষ ফলাফল হল যে আপনি আপনার কোডে foo(...) ব্যবহার করে উদ্বেগহীন (অনেক [1]) তার আর্গুমেন্টের ধরন সম্পর্কে ব্যবহার করতে পারেন।

আরো জটিল পরিস্থিতিগুলির জন্য, যেমন, বড় সংখ্যক আর্গুমেন্টগুলি বা বিভিন্ন সংখ্যক সংখ্যক সংখ্যক ক্রমবর্ধমান ফাংশন, আপনি স্বয়ংক্রিয়ভাবে স্ট্যাটিক ডিসপ্যাচ কাঠামো তৈরি করতে ইউটিলিটি ম্যাক্রো ব্যবহার করতে পারেন:

void print_ii(int a, int b) { printf("int, int\n"); }
void print_di(double a, int b) { printf("double, int\n"); }
void print_iii(int a, int b, int c) { printf("int, int, int\n"); }
void print_default(void) { printf("unknown arguments\n"); }

#define print(...) OVERLOAD(print, (__VA_ARGS__), \
    (print_ii, (int, int)), \
    (print_di, (double, int)), \
    (print_iii, (int, int, int)) \
)

#define OVERLOAD_ARG_TYPES (int, double)
#define OVERLOAD_FUNCTIONS (print)
#include "activate-overloads.h"

int main(void) {
    print(44, 47);   // prints "int, int"
    print(4.4, 47);  // prints "double, int"
    print(1, 2, 3);  // prints "int, int, int"
    print("");       // prints "unknown arguments"
}

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

একটি সরাইয়া হিসাবে, এটি ইতিমধ্যে 9999 সালে আর্গুমেন্ট সংখ্যা (টাইপ) উপর overload করা সম্ভব ছিল ।

[1] যেভাবে C মূল্যগুলি মূল্যায়ন করে ততক্ষণ পর্যন্ত আপনি ভ্রমণ করতে পারেন। উদাহরণস্বরূপ, এটি foo_int নির্বাচন করবে যদি আপনি এটির অক্ষরটি আক্ষরিক অক্ষরটি পাস করার চেষ্টা করেন, উদাহরণস্বরূপ, এবং যদি আপনার স্ট্রিং লিটারালগুলিকে সমর্থন করার জন্য আপনার ওভারলোডগুলি চান তবে আপনাকে কিছুটা foo_int করতে হবে। এখনও সামগ্রিক বেশ শান্ত যদিও।


Leushenko এর উত্তর সত্যিই শীতল - সম্পূর্ণরূপে: foo উদাহরণ GCC, যা foo(7) এ ব্যর্থ হয়, FIRST ম্যাক্রো এবং প্রকৃত ফাংশন কল ( (_1, __VA_ARGS__) , যা একটি উদ্বৃত্ত কমা দিয়ে অবশিষ্ট থাকে (_1, __VA_ARGS__) । উপরন্তু, যদি আমরা অতিরিক্ত অতিরিক্ত লোড সরবরাহ করতে চাই, যেমন foo(double)

তাই আমি উত্তরটি আরও বিস্তারিতভাবে বর্ণনা করার সিদ্ধান্ত নিয়েছি, যার মধ্যে একটি অকার্যকর ওভারলোড ( foo(void) - যা বেশ কিছু ঝামেলা সৃষ্টি করেছে ...)।

আইডিয়া এখন: বিভিন্ন ম্যাক্রোতে একাধিক জেনেরিক সংজ্ঞায়িত করুন এবং আর্গুমেন্ট সংখ্যা অনুযায়ী সঠিক নির্বাচন করুন!

আর্গুমেন্টগুলির সংখ্যাটি এই উত্তরটির উপর ভিত্তি করে বেশ সহজ:

#define foo(...) SELECT(__VA_ARGS__)(__VA_ARGS__)

#define SELECT(...) CONCAT(SELECT_, NARG(__VA_ARGS__))(__VA_ARGS__)
#define CONCAT(X, Y) CONCAT_(X, Y)
#define CONCAT_(X, Y) X ## Y

এটি চমৎকার, আমরা either SELECT_1 বা SELECT_2 (অথবা আরো আর্গুমেন্টগুলি, যদি আপনি চান / তাদের প্রয়োজন হয়) থেকে সমাধান করি, তাই আমাদের কেবল সঠিক SELECT_2 প্রয়োজন:

#define SELECT_0() foo_void
#define SELECT_1(_1) _Generic ((_1),    \
        int: foo_int,                   \
        char: foo_char,                 \
        double: foo_double              \
)
#define SELECT_2(_1, _2) _Generic((_1), \
        double: _Generic((_2),          \
                int: foo_double_int     \
        )                               \
)

ঠিক আছে, আমি ইতিমধ্যে অকার্যকর ওভারলোড যুক্ত করেছি - তবে, এটি আসলে সি মান দ্বারা আচ্ছাদিত নয়, যা খালি ভেরিয়েডিক আর্গুমেন্টগুলিকে অনুমতি দেয় না, অর্থাৎ আমরা কম্পাইলার এক্সটেনশনগুলিতে নির্ভর করি!

খুব প্রথমে, একটি খালি ম্যাক্রো কল ( foo() ) এখনও একটি টোকেন তৈরি করে, কিন্তু একটি খালি। সুতরাং গণনা ম্যাক্রো প্রকৃতপক্ষে খালি ম্যাক্রো কল থেকে 0 এর পরিবর্তে 1 প্রদান করে। আমরা এই সমস্যাটিকে "সহজে" মুছে ফেলতে পারি, যদি আমরা __VA_ARGS__ শর্তাধীনভাবে কমা __VA_ARGS__ তবে তালিকাটি খালি থাকা বা না থাকায়:

#define NARG(...) ARG4_(__VA_ARGS__ COMMA(__VA_ARGS__) 4, 3, 2, 1, 0)

যে সহজ লাগছিল , কিন্তু COMMA ম্যাক্রো বেশ ভারী এক; সৌভাগ্যবশত, বিষয় ইতিমধ্যে জেন্স Gustedt একটি ব্লগ আচ্ছাদিত (ধন্যবাদ, জেন)। মৌলিক কৌশলটি হল ফাংশন ম্যাক্রোগুলি যদি প্রসারিত না হয় তবে বন্ধনী দ্বারা অনুসরণ না করা, আরো ব্যাখ্যাগুলির জন্য, জেন্সের ব্লগটি দেখুন ... আমাদের কেবল আমাদের প্রয়োজনগুলির জন্য সামান্যতম ম্যাক্রো সংশোধন করতে হবে (আমি ছোট নামগুলি ব্যবহার করব এবং সংক্ষিপ্তত্ব জন্য কম আর্গুমেন্ট)।

#define ARGN(...) ARGN_(__VA_ARGS__)
#define ARGN_(_0, _1, _2, _3, N, ...) N
#define HAS_COMMA(...) ARGN(__VA_ARGS__, 1, 1, 1, 0)

#define SET_COMMA(...) ,

#define COMMA(...) SELECT_COMMA             \
(                                           \
        HAS_COMMA(__VA_ARGS__),             \
        HAS_COMMA(__VA_ARGS__ ()),          \
        HAS_COMMA(SET_COMMA __VA_ARGS__),   \
        HAS_COMMA(SET_COMMA __VA_ARGS__ ()) \
)

#define SELECT_COMMA(_0, _1, _2, _3) SELECT_COMMA_(_0, _1, _2, _3)
#define SELECT_COMMA_(_0, _1, _2, _3) COMMA_ ## _0 ## _1 ## _2 ## _3

#define COMMA_0000 ,
#define COMMA_0001
#define COMMA_0010 ,
// ... (all others with comma)
#define COMMA_1111 ,

এবং এখন আমরা ভাল আছি ...

এক ব্লকের সম্পূর্ণ কোড:

/*
 * demo.c
 *
 *  Created on: 2017-09-14
 *      Author: sboehler
 */

#include <stdio.h>

void foo_void(void)
{
    puts("void");
}
void foo_int(int c)
{
    printf("int: %d\n", c);
}
void foo_char(char c)
{
    printf("char: %c\n", c);
}
void foo_double(double c)
{
    printf("double: %.2f\n", c);
}
void foo_double_int(double c, int d)
{
    printf("double: %.2f, int: %d\n", c, d);
}

#define foo(...) SELECT(__VA_ARGS__)(__VA_ARGS__)

#define SELECT(...) CONCAT(SELECT_, NARG(__VA_ARGS__))(__VA_ARGS__)
#define CONCAT(X, Y) CONCAT_(X, Y)
#define CONCAT_(X, Y) X ## Y

#define SELECT_0() foo_void
#define SELECT_1(_1) _Generic ((_1), \
        int: foo_int,                \
        char: foo_char,              \
        double: foo_double           \
)
#define SELECT_2(_1, _2) _Generic((_1), \
        double: _Generic((_2),          \
                int: foo_double_int     \
        )                               \
)

#define ARGN(...) ARGN_(__VA_ARGS__)
#define ARGN_(_0, _1, _2, N, ...) N

#define NARG(...) ARGN(__VA_ARGS__ COMMA(__VA_ARGS__) 3, 2, 1, 0)
#define HAS_COMMA(...) ARGN(__VA_ARGS__, 1, 1, 0)

#define SET_COMMA(...) ,

#define COMMA(...) SELECT_COMMA             \
(                                           \
        HAS_COMMA(__VA_ARGS__),             \
        HAS_COMMA(__VA_ARGS__ ()),          \
        HAS_COMMA(SET_COMMA __VA_ARGS__),   \
        HAS_COMMA(SET_COMMA __VA_ARGS__ ()) \
)

#define SELECT_COMMA(_0, _1, _2, _3) SELECT_COMMA_(_0, _1, _2, _3)
#define SELECT_COMMA_(_0, _1, _2, _3) COMMA_ ## _0 ## _1 ## _2 ## _3

#define COMMA_0000 ,
#define COMMA_0001
#define COMMA_0010 ,
#define COMMA_0011 ,
#define COMMA_0100 ,
#define COMMA_0101 ,
#define COMMA_0110 ,
#define COMMA_0111 ,
#define COMMA_1000 ,
#define COMMA_1001 ,
#define COMMA_1010 ,
#define COMMA_1011 ,
#define COMMA_1100 ,
#define COMMA_1101 ,
#define COMMA_1110 ,
#define COMMA_1111 ,

int main(int argc, char** argv)
{
    foo();
    foo(7);
    foo(10.12);
    foo(12.10, 7);
    foo((char)'s');

    return 0;
}




overloading