সি তে একটি ভেরিয়াদিক ফাংশনের অনুরোধ ফরোয়ার্ড করুন


189

সি-তে, কোনও বৈকল্পিক ক্রিয়াকলাপের অনুরোধটি ফরোয়ার্ড করা সম্ভব? হিসাবে হিসাবে,

int my_printf(char *fmt, ...) {
    fprintf(stderr, "Calling printf with fmt %s", fmt);
    return SOMEHOW_INVOKE_LIBC_PRINTF;
}

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

[আপডেট: এখনও পর্যন্ত সরবরাহ করা উত্তরগুলির উপর ভিত্তি করে কিছু বিভ্রান্তি রয়েছে বলে মনে হচ্ছে। প্রশ্নটিকে অন্যভাবে বলার জন্য: সাধারণভাবে, আপনি সেই ফাংশনের সংজ্ঞাটি পরিবর্তন না করে কিছু স্বেচ্ছাসেবী বহিরাগত ফাংশনটি গুটিয়ে রাখতে পারেন ]]


1
দুর্দান্ত প্রশ্ন - ভাগ্যক্রমে আমি একটি ভিএফঙ্ক ওভারলোড যোগ করতে পারি যা একটি ভিএলিস্ট লাগে। পোস্ট করার জন্য ধন্যবাদ।
গিশু

উত্তর:


157

আপনি যদি একটি ফাংশন অনুরূপ না থাকে তাহলে করতে vfprintfএকটি লাগে va_listআর্গুমেন্ট একটি পরিবর্তনশীল সংখ্যা পরিবর্তে আপনি এটা ব্যবহার করতে পারবেন না । দেখাhttp://c-faq.com/varargs/handoff.html

উদাহরণ:

void myfun(const char *fmt, va_list argp) {
    vfprintf(stderr, fmt, argp);
}

5
সমস্যাটি কতটা গুরুত্বপূর্ণ, তার উপর নির্ভর করে ইনলাইন সমাবেশের দ্বারা প্রার্থনা এখনও এই সম্ভাবনাটি সরবরাহ করে।
ফিলিপ

60

সরাসরি নয়, তবে সাধারণ (এবং আপনি স্ট্যান্ডার্ড লাইব্রেরিতে প্রায় সর্বজনীন ক্ষেত্রে এটি দেখতে পাবেন) varargsস্টাইল বিকল্প ফাংশনটির সাথে জোড়ায় আসতে ভেরিয়াদিক ফাংশনগুলির জন্য । যেমন printf/vprintf

ভি ... ফাংশনগুলি একটি ভিএলিস্ট প্যারামিটার নেয়, যার বাস্তবায়ন প্রায়শই সংকলক নির্দিষ্ট 'ম্যাক্রো ম্যাজিক' দিয়ে সম্পন্ন হয় তবে আপনি গ্যারান্টিযুক্ত যে এই ধরণের একটি বৈকল্পিক ফাংশন থেকে v ... স্টাইল ফাংশনটি কাজ করবে:

#include <stdarg.h>

int m_printf(char *fmt, ...)
{
    int ret;

    /* Declare a va_list type variable */
    va_list myargs;

    /* Initialise the va_list variable with the ... after fmt */

    va_start(myargs, fmt);

    /* Forward the '...' to vprintf */
    ret = vprintf(fmt, myargs);

    /* Clean up the va_list */
    va_end(myargs);

    return ret;
}

এটি আপনার সন্ধান করছে এমন প্রভাব দেয়।

আপনি যদি ভ্যারিয়েডিক লাইব্রেরি ফাংশন লেখার বিষয়ে বিবেচনা করে থাকেন তবে আপনারও লাইব্রেরির অংশ হিসাবে একটি ভিএলিস্ট শৈলীর সহযোগী উপলব্ধ করার বিষয়টি বিবেচনা করা উচিত। আপনার প্রশ্ন থেকে আপনি দেখতে পাচ্ছেন যে এটি আপনার ব্যবহারকারীদের পক্ষে কার্যকর প্রমাণিত হতে পারে।


আমি দৃ sure়ভাবে নিশ্চিত যে অন্য ফাংশনে ভেরিয়েবলটি va_copyপাস করার আগে আপনাকে কল করতে হবে myargs। দয়া করে এমএসসি 39-সি দেখুন , যেখানে এটি উল্লেখ করেছে যে আপনি যা করছেন তা অপরিজ্ঞাত আচরণ।
এভিগিগিয়ানো

2
@aviggiano: নিয়ম "কল না va_arg()একটি উপর va_list, আমি না যে, কারণ আমি ব্যবহার না একটি অনির্দিষ্ট মূল্য আছে যে," va_argআমার আহ্বান ফাংশনে। মান এর myargsথেকে কল (এই ক্ষেত্রে) এর পর vprintfহয় অনির্দিষ্ট (বলা যাচ্ছে যে এটা আমাদের আছে va_arg)। মানদণ্ডে বলা হয়েছে যে myargs" va_end[এর] আরও কোনও রেফারেন্সের আগে ম্যাক্রোতে স্থান দেওয়া হবে"; যা আমি ঠিক তাই করি। আমার সেই আরগগুলি অনুলিপি করার দরকার নেই কারণ কলিং ফাংশনে তাদের মাধ্যমে পুনরাবৃত্তি করার আমার কোনও ইচ্ছা নেই।
সিবি বেইলি

1
তুমি ঠিক. লিনাক্স মানুষ যে পৃষ্ঠাটি নিশ্চিত। যদি apএমন কোনও ফাংশনে প্রেরণ করা হয় যা ব্যবহার করে va_arg(ap,type)তবে apসেই ফাংশনটির ফিরে আসার পরে এর মান নির্ধারিত হয় ।
এভিগিগিয়ানো

47

সি 99 বৈচিত্র্যপূর্ণ আর্গুমেন্ট সহ ম্যাক্রোগুলিকে সমর্থন করে ; আপনার সংকলকটির উপর নির্ভর করে আপনি একটি ম্যাক্রো ঘোষণা করতে সক্ষম হতে পারেন যা আপনি যা চান তা করেন:

#define my_printf(format, ...) \
    do { \
        fprintf(stderr, "Calling printf with fmt %s\n", format); \
        some_other_variadac_function(format, ##__VA_ARGS__); \
    } while(0)

সাধারণভাবে, তবে, সবচেয়ে ভাল সমাধানটি হ'ল যে ফাংশনটি আপনি মোড়ানোর চেষ্টা করছেন তার ভিএলিস্ট ফর্মটি ব্যবহার করা উচিত, এটির একটি উপস্থিত থাকা উচিত।


12

প্রায় উপলব্ধ সুবিধাগুলি ব্যবহার করে <stdarg.h>:

#include <stdarg.h>
int my_printf(char *format, ...)
{
   va_list args;
   va_start(args, format);
   int r = vprintf(format, args);
   va_end(args);
   return r;
}

মনে রাখবেন যে আপনার vprintfপ্লেইন না করে সংস্করণটি ব্যবহার করতে হবে printf। ব্যবহার না করে এই পরিস্থিতিতে সরাসরি কোনও ভেরিয়াদিক ফাংশন কল করার উপায় নেই va_list


1
ধন্যবাদ, তবে এই প্রশ্নটি থেকে: "আমি যে কোডবেসে কাজ করছি তার মধ্যে [...] ভিএফপ্রিন্টফের মত একটি সহায়ক ফাংশন নেই (এবং যোগ করতে পারে না)"।
প্যাট্রিক

11

এই ধরণের কলগুলি সুন্দর উপায়ে ফরোয়ার্ড করা সত্যিই সম্ভব নয়, তাই আমরা মূল স্ট্যাক ফ্রেমের একটি অনুলিপি সহ একটি নতুন স্ট্যাক ফ্রেম স্থাপন করে এটিকে ঘিরে কাজ করেছি। তবে এটি অত্যন্ত অপ্রয়োজনীয় এবং সব ধরণের অনুমান করে তোলে যেমন কোডটি ফ্রেম পয়েন্টার এবং 'স্ট্যান্ডার্ড' কলিং কনভেনশন ব্যবহার করে।

এই শিরোলেখ ফাইলটি x86_64 এবং i386 (জিসিসি) এর জন্য ভেরিয়াদিক ফাংশনগুলিকে মোড়ানোর অনুমতি দেয়। এটি ভাসমান-পয়েন্ট আর্গুমেন্টের জন্য কাজ করে না, তবে তাদের সমর্থন করার জন্য প্রসারিত করার জন্য সরাসরি এগিয়ে থাকা উচিত।

#ifndef _VA_ARGS_WRAPPER_H
#define _VA_ARGS_WRAPPER_H
#include <limits.h>
#include <stdint.h>
#include <alloca.h>
#include <inttypes.h>
#include <string.h>

/* This macros allow wrapping variadic functions.
 * Currently we don't care about floating point arguments and
 * we assume that the standard calling conventions are used.
 *
 * The wrapper function has to start with VA_WRAP_PROLOGUE()
 * and the original function can be called by
 * VA_WRAP_CALL(function, ret), whereas the return value will
 * be stored in ret.  The caller has to provide ret
 * even if the original function was returning void.
 */

#define __VA_WRAP_CALL_FUNC __attribute__ ((noinline))

#define VA_WRAP_CALL_COMMON()                                        \
    uintptr_t va_wrap_this_bp,va_wrap_old_bp;                        \
    va_wrap_this_bp  = va_wrap_get_bp();                             \
    va_wrap_old_bp   = *(uintptr_t *) va_wrap_this_bp;               \
    va_wrap_this_bp += 2 * sizeof(uintptr_t);                        \
    size_t volatile va_wrap_size = va_wrap_old_bp - va_wrap_this_bp; \
    uintptr_t *va_wrap_stack = alloca(va_wrap_size);                 \
    memcpy((void *) va_wrap_stack,                                   \
        (void *)(va_wrap_this_bp), va_wrap_size);


#if ( __WORDSIZE == 64 )

/* System V AMD64 AB calling convention */

static inline uintptr_t __attribute__((always_inline)) 
va_wrap_get_bp()
{
    uintptr_t ret;
    asm volatile ("mov %%rbp, %0":"=r"(ret));
    return ret;
}


#define VA_WRAP_PROLOGUE()           \
    uintptr_t va_wrap_ret;           \
    uintptr_t va_wrap_saved_args[7]; \
    asm volatile  (                  \
    "mov %%rsi,     (%%rax)\n\t"     \
    "mov %%rdi,  0x8(%%rax)\n\t"     \
    "mov %%rdx, 0x10(%%rax)\n\t"     \
    "mov %%rcx, 0x18(%%rax)\n\t"     \
    "mov %%r8,  0x20(%%rax)\n\t"     \
    "mov %%r9,  0x28(%%rax)\n\t"     \
    :                                \
    :"a"(va_wrap_saved_args)         \
    );

#define VA_WRAP_CALL(func, ret)            \
    VA_WRAP_CALL_COMMON();                 \
    va_wrap_saved_args[6] = (uintptr_t)va_wrap_stack;  \
    asm volatile (                         \
    "mov      (%%rax), %%rsi \n\t"         \
    "mov   0x8(%%rax), %%rdi \n\t"         \
    "mov  0x10(%%rax), %%rdx \n\t"         \
    "mov  0x18(%%rax), %%rcx \n\t"         \
    "mov  0x20(%%rax),  %%r8 \n\t"         \
    "mov  0x28(%%rax),  %%r9 \n\t"         \
    "mov           $0, %%rax \n\t"         \
    "call             *%%rbx \n\t"         \
    : "=a" (va_wrap_ret)                   \
    : "b" (func), "a" (va_wrap_saved_args) \
    :  "%rcx", "%rdx",                     \
      "%rsi", "%rdi", "%r8", "%r9",        \
      "%r10", "%r11", "%r12", "%r14",      \
      "%r15"                               \
    );                                     \
    ret = (typeof(ret)) va_wrap_ret;

#else

/* x86 stdcall */

static inline uintptr_t __attribute__((always_inline))
va_wrap_get_bp()
{
    uintptr_t ret;
    asm volatile ("mov %%ebp, %0":"=a"(ret));
    return ret;
}

#define VA_WRAP_PROLOGUE() \
    uintptr_t va_wrap_ret;

#define VA_WRAP_CALL(func, ret)        \
    VA_WRAP_CALL_COMMON();             \
    asm volatile (                     \
    "mov    %2, %%esp \n\t"            \
    "call  *%1        \n\t"            \
    : "=a"(va_wrap_ret)                \
    : "r" (func),                      \
      "r"(va_wrap_stack)               \
    : "%ebx", "%ecx", "%edx"   \
    );                                 \
    ret = (typeof(ret))va_wrap_ret;
#endif

#endif

শেষ পর্যন্ত আপনি এইভাবে কলগুলি মোড়ানো করতে পারেন:

int __VA_WRAP_CALL_FUNC wrap_printf(char *str, ...)
{
    VA_WRAP_PROLOGUE();
    int ret;
    VA_WRAP_CALL(printf, ret);
    printf("printf returned with %d \n", ret);
    return ret;
}

3

ভিএফপ্রিন্টফ ব্যবহার করুন:

int my_printf(char *fmt, ...) {
    va_list va;
    int ret;

    va_start(va, fmt);
    ret = vfprintf(stderr, fmt, va);
    va_end(va);
    return ret;
}

1
ধন্যবাদ, তবে এই প্রশ্নটি থেকে: "আমি যে কোডবেসে কাজ করছি তার মধ্যে [...] ভিএফপ্রিন্টফের মত একটি সহায়ক ফাংশন নেই (এবং যোগ করতে পারে না)"।
প্যাট্রিক

2

এই ধরনের ফাংশন কলগুলি ফরোয়ার্ড করার কোনও উপায় নেই কারণ আপনি যেখানে কাঁচা স্ট্যাক উপাদানগুলি পুনরুদ্ধার করতে পারেন কেবলমাত্র সেই অবস্থানটি my_print()। এর মতো কলগুলিকে মোড়ানোর স্বাভাবিক উপায় দুটি ফাংশন হ'ল একটি, যা কেবল যুক্তিকে বিভিন্ন varargsস্ট্রকে রূপান্তরিত করে এবং অন্যটি যা সেই স্ট্রাকগুলিতে আসলে কাজ করে। যেমন একটি ডাবল ফাংশন মডেল ব্যবহার করে, আপনি (উদাহরণস্বরূপ) printf()এর my_printf()সাথে স্ট্রাক্টগুলি শুরু করে মোড়ানো করতে পারেন va_start()এবং তারপরে এটিকে পাস করতে পারেন vfprintf()


সুতরাং আপনি মোড়ানো করতে চান ফাংশনটির বাস্তবায়ন আপনি যদি পরিবর্তন করতে না পারেন তবে অনুরোধটি ফরোয়ার্ড করার কোনও উপায় নেই?
প্যাট্রিক

ঠিক। আপনার সেরা বাজি হ'ল একটি ভিপ্রিন্টফ-স্টাইলের মোড়ক যুক্ত করা for
জন মিলিকিন

1

হ্যাঁ আপনি এটি করতে পারেন, তবে এটি কিছুটা কুরুচিপূর্ণ এবং আপনাকে আর্গুমেন্টের সর্বাধিক সংখ্যা জানতে হবে। এছাড়াও আপনি যদি এমন কোনও আর্কিটেকচারে থাকেন যেখানে x86 (উদাহরণস্বরূপ, পাওয়ারপিসি) এর মতো স্ট্যাকের উপর আর্গুমেন্টগুলি পাস করা হয় না, আপনাকে "বিশেষ" প্রকারগুলি (ডাবল, ফ্লোটস, অ্যালটিভেক ইত্যাদি) ব্যবহার করা হয় কিনা তা জানতে হবে এবং যদি সুতরাং, তাদের সাথে সেই অনুযায়ী আচরণ করুন। এটি দ্রুত বেদনাদায়ক হতে পারে তবে আপনি যদি x86 এ থাকেন বা যদি মূল ফাংশনটির একটি সুস্পষ্ট সংজ্ঞাযুক্ত এবং সীমিত পরিধি থাকে তবে এটি কাজ করতে পারে। এটি এখনও একটি হ্যাক হবে , এটি ডিবাগিংয়ের উদ্দেশ্যে ব্যবহার করুন। আপনার চারপাশে আপনাকে সফ্টওয়্যার তৈরি করবেন না। যাইহোক, এখানে x86 এর একটি কার্যকারী উদাহরণ:

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

int old_variadic_function(int n, ...)
{
  va_list args;
  int i = 0;

  va_start(args, n);

  if(i++<n) printf("arg %d is 0x%x\n", i, va_arg(args, int));
  if(i++<n) printf("arg %d is %g\n",   i, va_arg(args, double));
  if(i++<n) printf("arg %d is %g\n",   i, va_arg(args, double));

  va_end(args);

  return n;
}

int old_variadic_function_wrapper(int n, ...)
{
  va_list args;
  int a1;
  int a2;
  int a3;
  int a4;
  int a5;
  int a6;
  int a7;
  int a8;

  /* Do some work, possibly with another va_list to access arguments */

  /* Work done */

  va_start(args, n);

  a1 = va_arg(args, int);
  a2 = va_arg(args, int);
  a3 = va_arg(args, int);
  a4 = va_arg(args, int);
  a5 = va_arg(args, int);
  a6 = va_arg(args, int);
  a7 = va_arg(args, int);

  va_end(args);

  return old_variadic_function(n, a1, a2, a3, a4, a5, a6, a7, a8);
}

int main(void)
{
  printf("Call 1: 1, 0x123\n");
  old_variadic_function(1, 0x123);
  printf("Call 2: 2, 0x456, 1.234\n");
  old_variadic_function(2, 0x456, 1.234);
  printf("Call 3: 3, 0x456, 4.456, 7.789\n");
  old_variadic_function(3, 0x456, 4.456, 7.789);
  printf("Wrapped call 1: 1, 0x123\n");
  old_variadic_function_wrapper(1, 0x123);
  printf("Wrapped call 2: 2, 0x456, 1.234\n");
  old_variadic_function_wrapper(2, 0x456, 1.234);
  printf("Wrapped call 3: 3, 0x456, 4.456, 7.789\n");
  old_variadic_function_wrapper(3, 0x456, 4.456, 7.789);

  return 0;
}

কোনও কারণে, আপনি va_arg সহ ফ্লোট ব্যবহার করতে পারবেন না, জিসিসি বলেছেন যে এগুলি দ্বিগুণ রূপান্তরিত হয়েছে তবে প্রোগ্রামটি ক্র্যাশ হয়ে গেছে। এটি একাই প্রমাণ করে যে এই সমাধানটি হ্যাক এবং এর কোনও সাধারণ সমাধান নেই। আমার উদাহরণে আমি ধরে নিয়েছি যে আর্গুমেন্টের সর্বাধিক সংখ্যা ছিল 8, তবে আপনি এই সংখ্যাটি বাড়াতে পারেন। মোড়ানো ফাংশনটি কেবলমাত্র পূর্ণসংখ্যার ব্যবহার করে তবে এটি অন্যান্য 'স্বাভাবিক' পরামিতিগুলির সাথে একইভাবে কাজ করে যেহেতু তারা সবসময় পূর্ণসংখ্যায় ফেলে দেয়। লক্ষ্য ফাংশনটি তাদের প্রকারগুলি জানতে পারবে তবে আপনার মধ্যস্থতার মোড়কের দরকার নেই doesn't মোড়কের ক্ষেত্রে সঠিক ক্রিয়াকলাপটি এটিও জেনে নিবে কারণ যুক্তিগুলির সঠিক সংখ্যাটিও জানতে হবে না। দরকারী কাজ করতে (কেবলমাত্র কলটি লগ করা ব্যতীত), সম্ভবত আপনাকে উভয়ই জানতে হবে।


0

মূলত তিনটি বিকল্প রয়েছে।

একটি হ'ল এটি পাস করা নয় তবে আপনার টার্গেট ফাংশনের বৈচিত্র্যময় প্রয়োগ প্রয়োগ করা এবং উপবৃত্তগুলিতে পাস না করা। অন্যটি হ'ল একটি ভেরিয়েডিক ম্যাক্রো ব্যবহার করা। তৃতীয় বিকল্পটি হ'ল সমস্ত জিনিস যা আমি নিখোঁজ করছি।

আমি সাধারণত একটি বিকল্পের সাথে যাই কারণ আমার মনে হয় এটি হ্যান্ডেল করা সত্যিই সহজ। বিকল্প দুটিটির একটি অপূর্ণতা রয়েছে কারণ ভেরিয়াদিক ম্যাক্রোগুলি কল করার কিছু সীমাবদ্ধতা রয়েছে।

এখানে কিছু উদাহরণ কোড দেওয়া হল:

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

#define Option_VariadicMacro(f, ...)\
    printf("printing using format: %s", f);\
    printf(f, __VA_ARGS__)

int Option_ResolveVariadicAndPassOn(const char * f, ... )
{
    int r;
    va_list args;

    printf("printing using format: %s", f);
    va_start(args, f);
    r = vprintf(f, args);
    va_end(args);
    return r;
}

void main()
{
    const char * f = "%s %s %s\n";
    const char * a = "One";
    const char * b = "Two";
    const char * c = "Three";
    printf("---- Normal Print ----\n");
    printf(f, a, b, c);
    printf("\n");
    printf("---- Option_VariadicMacro ----\n");
    Option_VariadicMacro(f, a, b, c);
    printf("\n");
    printf("---- Option_ResolveVariadicAndPassOn ----\n");
    Option_ResolveVariadicAndPassOn(f, a, b, c);
    printf("\n");
}

0

এটি করার সবচেয়ে ভাল উপায়

static BOOL(__cdecl *OriginalVarArgsFunction)(BYTE variable1, char* format, ...)(0x12345678); //TODO: change address lolz

BOOL __cdecl HookedVarArgsFunction(BYTE variable1, char* format, ...)
{
    BOOL res;

    va_list vl;
    va_start(vl, format);

    // Get variable arguments count from disasm. -2 because of existing 'format', 'variable1'
    uint32_t argCount = *((uint8_t*)_ReturnAddress() + 2) / sizeof(void*) - 2;
    printf("arg count = %d\n", argCount);

    // ((int( __cdecl* )(const char*, ...))&oldCode)(fmt, ...);
    __asm
    {
        mov eax, argCount
        test eax, eax
        je noLoop
        mov edx, vl
        loop1 :
        push dword ptr[edx + eax * 4 - 4]
        sub eax, 1
        jnz loop1
        noLoop :
        push format
        push variable1
        //lea eax, [oldCode] // oldCode - original function pointer
        mov eax, OriginalVarArgsFunction
        call eax
        mov res, eax
        mov eax, argCount
        lea eax, [eax * 4 + 8] //+8 because 2 parameters (format and variable1)
        add esp, eax
    }
    return res;
}

0

জিসিসি একটি এক্সটেনশন দেয় যা এটি করতে পারে: __builtin_applyএবং আত্মীয়স্বজন। গিসি ম্যানুয়ালটিতে ফাংশন কলগুলি নির্মাণের দেখুন ।

একটি উদাহরণ:

#include <stdio.h>

int my_printf(const char *fmt, ...) {
    void *args = __builtin_apply_args();
    printf("Hello there! Format string is %s\n", fmt);
    void *ret = __builtin_apply((void (*)())printf, args, 1000);
    __builtin_return(ret);
}

int main(void) {
    my_printf("%d %f %s\n", -37, 3.1415, "spam");
    return 0;
}

গডবোল্টে চেষ্টা করুন

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


0

নিশ্চিত না যে এটি ওপি-র প্রশ্নের উত্তর দিতে সহায়তা করে কারণ আমি জানি না কেন মোড়ক ফাংশনে ভিএফপ্রিন্টফের সাহায্যে কোনও সহায়ক ফাংশন ব্যবহারের ক্ষেত্রে নিষেধাজ্ঞা কেন প্রযোজ্য। আমি মনে করি এখানে মূল সমস্যাটি হ'ল বৈচিত্র্যপূর্ণ যুক্তি তালিকার তাদের ব্যাখ্যা না করে ফরোয়ার্ড করা কঠিন is যা সম্ভব তা হ'ল বিন্যাস সম্পাদন করা (ভিএফপ্রিন্টফের মত হেল্পার ফাংশন ব্যবহার করে: ভার্সনপ্রিন্টফ) এবং ফরমেটেড আউটপুটটি ভের্যাডিক আর্গুমেন্টের সাহায্যে মোড়ানো ফাংশনে ফরোয়ার্ড করা (যেমন মোড়িত ফাংশনটির সংজ্ঞা পরিবর্তন না করা)) সুতরাং, আমরা এখানে যাই:

int my_printf(char *fmt, ...)
{
    int ret;

    if (fmt == NULL) {
        /* Invalid format pointer */
        ret = -1;
    } else {
        va_list args;
        int len;

        va_start(args, fmt);

        /* Get length of format including arguments */
        len = vsnprintf(NULL, 0, fmt, args);

        if (len < 0) {
            /* vsnprintf failed */
            ret = -1;
        } else {
            /* Declare a character buffer and write the formatted string */
            char formatted[len + 1];
            vsnprintf(formatted, sizeof(formatted), fmt, args);

            /* Call the wrapped function using the formatted output and return */
            fprintf(stderr, "Calling printf with fmt %s", fmt);
            ret = printf(formatted);
        }

        va_end(args);
    }

    return ret;
}

আমি এখানে এই সমাধান জুড়ে এসেছি

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