একটি ম্যাচ ফাংশন পয়েন্টার কল করতে একটি টিপল "আনপ্যাকিং"


253

আমি std::tupleবিভিন্ন ধরণের মানগুলিতে সঞ্চয় করার চেষ্টা করছি , যা পরে কোনও ফাংশন পয়েন্টারে কল করার জন্য আর্গুমেন্ট হিসাবে ব্যবহৃত হবে যা সঞ্চিত ধরণের সাথে মেলে।

আমি যে সমস্যার সমাধান করতে চাইছি তা দেখিয়ে একটি সরল উদাহরণ তৈরি করেছি:

#include <iostream>
#include <tuple>

void f(int a, double b, void* c) {
  std::cout << a << ":" << b << ":" << c << std::endl;
}

template <typename ...Args>
struct save_it_for_later {
  std::tuple<Args...> params;
  void (*func)(Args...);

  void delayed_dispatch() {
     // How can I "unpack" params to call func?
     func(std::get<0>(params), std::get<1>(params), std::get<2>(params));
     // But I *really* don't want to write 20 versions of dispatch so I'd rather 
     // write something like:
     func(params...); // Not legal
  }
};

int main() {
  int a=666;
  double b = -1.234;
  void *c = NULL;

  save_it_for_later<int,double,void*> saved = {
                                 std::tuple<int,double,void*>(a,b,c), f};
  saved.delayed_dispatch();
}

সাধারণত জড়িত std::tupleবা বৈকল্পিক টেমপ্লেটগুলির সমস্যার জন্য আমি অন্য একটি টেমপ্লেট লিখি যেমন template <typename Head, typename ...Tail>একের পর এক ধরণের সমস্ত ধরণের পুনরাবৃত্তিমূলক মূল্যায়ন করতে চাই তবে ফাংশন কল প্রেরণের জন্য আমি এটি করার কোনও উপায় দেখতে পাচ্ছি না।

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

কলটি প্রেরণের একটি পরিষ্কার উপায় কী std::tupleবা কোনও স্বেচ্ছাসেবী ভবিষ্যতের পয়েন্ট অবধি কিছু মান এবং ফাংশন পয়েন্টার সংরক্ষণ / ফরোয়ার্ড করার একই নেট ফলাফল অর্জনের বিকল্প উপায়?


5
আপনি কেন কেবল ব্যবহার করতে পারবেন না auto saved = std::bind(f, a, b, c);... তবে পরে কেবল কল করুন saved()?
চার্লস সালভিয়া

সবসময় নিয়ন্ত্রণ করার জন্য আমার ইন্টারফেস নয়। আমি অন্য কারও কাছ থেকে চুক্তি করে একটি টিপল পেয়েছি এবং পরে এটির সাথে জিনিসগুলি করতে চাই।
ফ্লেক্সো

উত্তর:


275

আপনাকে সংখ্যার একটি প্যারামিটার প্যাক তৈরি করতে হবে এবং সেগুলি আনপ্যাক করতে হবে

template<int ...>
struct seq { };

template<int N, int ...S>
struct gens : gens<N-1, N-1, S...> { };

template<int ...S>
struct gens<0, S...> {
  typedef seq<S...> type;
};


// ...
  void delayed_dispatch() {
     callFunc(typename gens<sizeof...(Args)>::type());
  }

  template<int ...S>
  void callFunc(seq<S...>) {
     func(std::get<S>(params) ...);
  }
// ...

4
বাহ, আমি জানতাম না আনপ্যাকিং অপারেটরটি এর মতো ব্যবহার করা যেতে পারে, এটি দুর্দান্ত!
লুক টুরাইল

5
জোহানেস, আপনি এটি পোস্ট করার পরে আমি বুঝতে পেরেছি যে এটি 2+ বছর হয়ে গেছে, তবে আমি যে বিষয়টির সাথে লড়াই করছি তার মধ্যে struct gensজেনেরিক সংজ্ঞাটি রয়েছে (যা একই কথার বিস্তৃত উত্স থেকে উত্তরাধিকার সূত্রে প্রাপ্ত )। আমি দেখতে পাচ্ছি অবশেষে এটি 0 টির সাথে বিশেষতাকে হিট করেছে যদি মেজাজটি আপনার জন্য উপযুক্ত হয় এবং আপনার অতিরিক্ত চক্র থাকে তবে আপনি যদি এটির প্রসারিত করতে পারেন এবং কীভাবে এটি এর জন্য ব্যবহার করা হয় তবে আমি চির কৃতজ্ঞ থাকব। এবং আমি আশা করি আমি এটি একশবার বার ভোট দিতে পারতাম। আমি এই কোড থেকে স্পর্শকাতর সঙ্গে খেলা আরও মজা পেয়েছি। ধন্যবাদ।
WhozCraig

22
@ হোউজক্রাইগ: এটি যা করে তা এক ধরণের উত্পন্ন করে seq<0, 1, .., N-1>। এটা কিভাবে কাজ করে: gens<5>: gens<4, 4>: gens<3, 3, 4>: gens<2, 2, 3, 4> : gens<1, 1, 2, 3, 4> : gens<0, 0, 1, 2, 3, 4>। শেষ প্রকারটি বিশেষায়িত, তৈরি করা হচ্ছে seq<0, 1, 2, 3, 4>। খুব চতুর কৌশল।
মাইন্ডভাইরাস

2
@ নিরিফ্রিডম্যান: অবশ্যই, কেবলমাত্র এর অনির্দিষ্ট সংস্করণটি প্রতিস্থাপন করুন gens:template <int N, int... S> struct gens { typedef typename gens<N-1, N-1, S...>::type type; };
মার্টন 78

11
ওয়াল্টারের উত্তর এবং এর উপর মন্তব্যগুলি প্রতিধ্বনিত করার মতো: লোকদের আর তাদের নিজের চাকা আবিষ্কার করার দরকার নেই। একটি ক্রম তৈরি করা হচ্ছে, যাতে সাধারণ এটি যেমন C ++ 14 আদর্শায়িত ছিল std::integer_sequence<T, N>এবং বিশেষজ্ঞতা উহার std::size_t, std::index_sequence<N>প্লাস তাদের সংযুক্ত সাহায্যকারী ফাংশন - std::make_in(teger|dex)_sequence<>()এবং std::index_sequence_for<Ts...>()। এবং সি ++ 17 এ লাইব্রেরিতে আরও অনেক ভাল জিনিস একত্রিত হয়েছে - বিশেষত অন্তর্ভুক্ত std::applyএবং std::make_from_tuple, যা আনপ্যাকিং এবং কলিং বিটগুলি পরিচালনা করবে
আন্ডারস্কোর_আডেম্বর

61

সি ++ 17 সমাধানটি কেবল ব্যবহারের জন্য std::apply:

auto f = [](int a, double b, std::string c) { std::cout<<a<<" "<<b<<" "<<c<< std::endl; };
auto params = std::make_tuple(1,2.0,"Hello");
std::apply(f, params);

কেবল অনুভূত হয়েছে যে এই থ্রেডের উত্তরে একবার এটি বলা উচিত (এটি ইতিমধ্যে কোনও মন্তব্যে প্রকাশিত হওয়ার পরে)।


বেসিক সি ++ 14 সমাধানটি এখনও এই থ্রেডে অনুপস্থিত। সম্পাদনা: না, ওয়ালটারের উত্তরে এটি আসলে আছে।

এই ফাংশন দেওয়া হয়:

void f(int a, double b, void* c)
{
      std::cout << a << ":" << b << ":" << c << std::endl;
}

নিম্নলিখিত স্নিপেট দিয়ে এটিকে কল করুন:

template<typename Function, typename Tuple, size_t ... I>
auto call(Function f, Tuple t, std::index_sequence<I ...>)
{
     return f(std::get<I>(t) ...);
}

template<typename Function, typename Tuple>
auto call(Function f, Tuple t)
{
    static constexpr auto size = std::tuple_size<Tuple>::value;
    return call(f, t, std::make_index_sequence<size>{});
}

উদাহরণ:

int main()
{
    std::tuple<int, double, int*> t;
    //or std::array<int, 3> t;
    //or std::pair<int, double> t;
    call(f, t);    
}

ডেমো


আমি স্মার্ট পয়েন্টার নিয়ে কাজ করতে এই ডেমোটি পেতে পারি না - এখানে কী ভুল? http://coliru.stacked-crooked.com/a/8ea8bcc878efc3cb
জেভেরিয়াস

@ Xeverous: আপনি কি এখানে এখানে কিছু পেতে চান ?
দবিধিঃ

ধন্যবাদ, আমার 2 টি প্রশ্ন রয়েছে: 1. কেন আমি std::make_uniqueসরাসরি পাস করতে পারি না ? এটির জন্য কি কংক্রিটের ফাংশন উদাহরণ প্রয়োজন? ২. কেন std::move(ts)...আমরা বদলে [](auto... ts)যেতে [](auto&&... ts)পারি?
জেভেরিয়াস

@ Xeverous: 1. স্বাক্ষরগুলি থেকে কাজ করে না: আপনার std::make_uniqueএকটি টিপল প্রত্যাশা করে, এবং কেবল একটি অন্য কলের মাধ্যমে একটি প্যাকযুক্ত টুপল থেকে একটি টিউপল তৈরি করা যেতে পারে std::make_tuple। ল্যাম্বডায় এটিই আমি করেছি (যদিও এটি অত্যন্ত নিরানন্দ, যদিও আপনি কেবল কোনও ব্যবহার ছাড়াই অনন্য পয়েন্টারে টুপলটি অনুলিপি করতে পারেন call)।
দবিধিঃ

1
এটি এখন হওয়া উচিত উত্তর।
ফিউরিশ

44

এটি জোহানেসদের আউডল্যান্ডের প্রশ্নের সমাধানের সম্পূর্ণ সংকলনীয় সংস্করণ , এই আশায় এটি কারওর পক্ষে কার্যকর হতে পারে। এটি দেবিয়ান স্কুজে জি ++ 4.7 এর একটি স্ন্যাপশটের মাধ্যমে পরীক্ষা করা হয়েছিল।

###################
johannes.cc
###################
#include <tuple>
#include <iostream>
using std::cout;
using std::endl;

template<int ...> struct seq {};

template<int N, int ...S> struct gens : gens<N-1, N-1, S...> {};

template<int ...S> struct gens<0, S...>{ typedef seq<S...> type; };

double foo(int x, float y, double z)
{
  return x + y + z;
}

template <typename ...Args>
struct save_it_for_later
{
  std::tuple<Args...> params;
  double (*func)(Args...);

  double delayed_dispatch()
  {
    return callFunc(typename gens<sizeof...(Args)>::type());
  }

  template<int ...S>
  double callFunc(seq<S...>)
  {
    return func(std::get<S>(params) ...);
  }
};

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter"
#pragma GCC diagnostic ignored "-Wunused-variable"
#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
int main(void)
{
  gens<10> g;
  gens<10>::type s;
  std::tuple<int, float, double> t = std::make_tuple(1, 1.2, 5);
  save_it_for_later<int,float, double> saved = {t, foo};
  cout << saved.delayed_dispatch() << endl;
}
#pragma GCC diagnostic pop

যে কেউ নিম্নলিখিত স্ক্যানস্ট্রাক্ট ফাইলটি ব্যবহার করতে পারেন

#####################
SConstruct
#####################
#!/usr/bin/python

env = Environment(CXX="g++-4.7", CXXFLAGS="-Wall -Werror -g -O3 -std=c++11")
env.Program(target="johannes", source=["johannes.cc"])

আমার মেশিনে, এটি দেয়

g++-4.7 -o johannes.o -c -Wall -Werror -g -O3 -std=c++11 johannes.cc
g++-4.7 -o johannes johannes.o

আপনার কেন ভেরিয়েবল এস এবং জি দরকার?
shoosh

@ শুশ আমি অনুমান করি যে তাদের প্রয়োজন নেই। আমি ভুলে গেছি কেন আমি সেগুলি যুক্ত করেছি; প্রায় তিন বছর কেটে গেছে। তবে আমি মনে করি, ইনস্ট্যান্টেশন কাজ করে তা দেখানোর জন্য।
ফাহিম মিঠা

42

এখানে একটি সি ++ 14 সমাধান।

template <typename ...Args>
struct save_it_for_later
{
  std::tuple<Args...> params;
  void (*func)(Args...);

  template<std::size_t ...I>
  void call_func(std::index_sequence<I...>)
  { func(std::get<I>(params)...); }
  void delayed_dispatch()
  { call_func(std::index_sequence_for<Args...>{}); }
};

এটি এখনও একটি সহায়ক ফাংশন প্রয়োজন ( call_func)। যেহেতু এটি একটি সাধারণ প্রতিমা, সম্ভবত মানকটি সম্ভবত এটি std::callবাস্তবায়ন হিসাবে সরাসরি সমর্থন করা উচিত

// helper class
template<typename R, template<typename...> class Params, typename... Args, std::size_t... I>
R call_helper(std::function<R(Args...)> const&func, Params<Args...> const&params, std::index_sequence<I...>)
{ return func(std::get<I>(params)...); }

// "return func(params...)"
template<typename R, template<typename...> class Params, typename... Args>
R call(std::function<R(Args...)> const&func, Params<Args...> const&params)
{ return call_helper(func,params,std::index_sequence_for<Args...>{}); }

তারপরে আমাদের বিলম্বিত প্রেরণটি হয়ে যায়

template <typename ...Args>
struct save_it_for_later
{
  std::tuple<Args...> params;
  std::function<void(Args...)> func;
  void delayed_dispatch()
  { std::call(func,params); }
};

8
(প্রস্তাবিত) বাস্তবায়নের জন্য উত্সাহিত std::call। সি ++ 14 এর বিশৃঙ্খলা চিড়িয়াখানা integer_sequenceএবং index_sequenceসহায়ক ধরণের এখানে ব্যাখ্যা করা হয়েছে: en.cppreferences.com/w/cpp/utility/integer_sequence এর সুস্পষ্ট অনুপস্থিতি লক্ষ্য করুন std::make_index_sequence(Args...), যে কারণে ওয়াল্টারকে ক্লানকিয়ার সিনট্যাক্সে বাধ্য করা হয়েছিল std::index_sequence_for<Args...>{}
কুক্সপ্লসোন

3
এবং স্পষ্টতই সি ++ ১ into তে 3/2016 সাল থেকে std :: প্রয়োগ ( ফানক
ddevienne

18

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


6

সমাধান। প্রথমত, কিছু ইউটিলিটি বয়লারপ্লেট:

template<std::size_t...Is>
auto index_over(std::index_sequence<Is...>){
  return [](auto&&f)->decltype(auto){
    return decltype(f)(f)( std::integral_constant<std::size_t, Is>{}... );
  };
}
template<std::size_t N>
auto index_upto(std::integral_constant<std::size_t, N> ={}){
  return index_over( std::make_index_sequence<N>{} );
}

এগুলি আপনাকে সংকলন-কাল পূর্ণসংখ্যার সিরিজ সহ একটি ল্যাম্বডাকে কল করতে দেয়।

void delayed_dispatch() {
  auto indexer = index_upto<sizeof...(Args)>();
  indexer([&](auto...Is){
    func(std::get<Is>(params)...);
  });
}

এবং আমরা সম্পন্ন।

index_uptoএবং index_overআপনাকে নতুন বাহ্যিক ওভারলোডগুলি তৈরি না করেই প্যারামিটার প্যাকগুলি নিয়ে কাজ করতে দেয়।

অবশ্যই, ইন আপনি শুধু

void delayed_dispatch() {
  std::apply( func, params );
}

এখন, যদি আমরা এটি পছন্দ করি আমরা লিখতে পারি:

namespace notstd {
  template<class T>
  constexpr auto tuple_size_v = std::tuple_size<T>::value;
  template<class F, class Tuple>
  decltype(auto) apply( F&& f, Tuple&& tup ) {
    auto indexer = index_upto<
      tuple_size_v<std::remove_reference_t<Tuple>>
    >();
    return indexer(
      [&](auto...Is)->decltype(auto) {
        return std::forward<F>(f)(
          std::get<Is>(std::forward<Tuple>(tup))...
        );
      }
    );
  }
}

তুলনামূলকভাবে সহজে এবং ক্লিনার পেতে সিনট্যাক্স জাহাজ প্রস্তুত।

void delayed_dispatch() {
  notstd::apply( func, params );
}

আপনার সংকলক আপগ্রেড এবং বব আপনার কাকা যখন ঠিক notstdসঙ্গে প্রতিস্থাপন std


std::apply<- আমার কানে সংগীত
ফ্লেক্সো

@ ফ্লেক্সো কেবল কিছুটা খাটো index_uptoএবং নমনীয়। ;) funcআর্গুমেন্টগুলির সাথে পিছনে index_uptoএবং std::applyযথাক্রমে কল করার চেষ্টা করুন । স্বীকার করা হয়েছে, কে হেক পিছনের দিকে টিপল থেকে কোনও ফাংশন শুরু করতে চায়।
ইয়াক্ক - অ্যাডাম নেভ্রামামন্ট

গৌণ বিন্দু: std::tuple_size_vসি ++ ১ is, সুতরাং সি ++ ১৪ টি সমাধানের জন্য যেটি প্রতিস্থাপন করতে হবেtypename std::tuple_size<foo>::value
বাসেলেন

@ বাসটেলেন আশা করি valueকোনও প্রকার নয়। তবে যেভাবেই ঠিক করা হয়েছে।
ইয়াক্ক - অ্যাডাম নেভ্রামামন্ট

@Yakk না, এটা sizeof...(Types)। আমি ছাড়া আপনার সমাধান পছন্দ typename
বাসেলেন

3

প্রদত্ত উত্তরের উপর ভিত্তি করে সমস্যাটির বিষয়ে আরও কিছু চিন্তা করা আমি একই সমস্যা সমাধানের অন্য একটি উপায় খুঁজে পেয়েছি:

template <int N, int M, typename D>
struct call_or_recurse;

template <typename ...Types>
struct dispatcher {
  template <typename F, typename ...Args>
  static void impl(F f, const std::tuple<Types...>& params, Args... args) {
     call_or_recurse<sizeof...(Args), sizeof...(Types), dispatcher<Types...> >::call(f, params, args...);
  }
};

template <int N, int M, typename D>
struct call_or_recurse {
  // recurse again
  template <typename F, typename T, typename ...Args>
  static void call(F f, const T& t, Args... args) {
     D::template impl(f, t, std::get<M-(N+1)>(t), args...);
  }
};

template <int N, typename D>
struct call_or_recurse<N,N,D> {
  // do the call
  template <typename F, typename T, typename ...Args>
  static void call(F f, const T&, Args... args) {
     f(args...);
  }
};

যার বাস্তবায়ন পরিবর্তনের প্রয়োজন delayed_dispatch():

  void delayed_dispatch() {
     dispatcher<Args...>::impl(func, params);
  }

এটি পুনরাবৃত্তভাবে std::tupleতার নিজস্ব পরামিতি প্যাক রূপান্তর করে কাজ করে । call_or_recurseরিয়েল কলটির সাথে পুনরাবৃত্তি সমাপ্ত করার জন্য বিশেষীকরণ হিসাবে প্রয়োজন, যা কেবলমাত্র প্যারামিটার প্যাকটি সরিয়ে আনবে।

আমি নিশ্চিত নই যে এটি যাইহোক একটি "আরও ভাল" সমাধানে রয়েছে তবে এটি সম্পর্কে চিন্তাভাবনা এবং সমাধানের এটি অন্য উপায়।


enable_ifআমার আগের সমাধানের চেয়ে তর্কযোগ্যভাবে সহজ কিছু গঠনের জন্য আপনি অন্য বিকল্প সমাধান হিসাবে ব্যবহার করতে পারেন :

#include <iostream>
#include <functional>
#include <tuple>

void f(int a, double b, void* c) {
  std::cout << a << ":" << b << ":" << c << std::endl;
}

template <typename ...Args>
struct save_it_for_later {
  std::tuple<Args...> params;
  void (*func)(Args...);

  template <typename ...Actual>
  typename std::enable_if<sizeof...(Actual) != sizeof...(Args)>::type
  delayed_dispatch(Actual&& ...a) {
    delayed_dispatch(std::forward<Actual>(a)..., std::get<sizeof...(Actual)>(params));
  }

  void delayed_dispatch(Args ...args) {
    func(args...);
  }
};

int main() {
  int a=666;
  double b = -1.234;
  void *c = NULL;

  save_it_for_later<int,double,void*> saved = {
                                 std::tuple<int,double,void*>(a,b,c), f};
  saved.delayed_dispatch();
}

প্রথম ওভারলোডটি টিপল থেকে আরও একটি যুক্তি নেয় এবং এটি একটি প্যারামিটার প্যাকের মধ্যে রাখে। দ্বিতীয় ওভারলোডটি একটি ম্যাচিং প্যারামিটার প্যাক নেয় এবং তারপরে আসল কল করে, প্রথম ওভারলোডটি একটিতে অক্ষম করা হয় এবং একমাত্র ক্ষেত্রে যেখানে দ্বিতীয়টি কার্যকর হয়।


1
আমি কিছুক্ষণ আগে এইরকম ভয়ঙ্কর কিছু নিয়ে কাজ করেছি। আমার যদি সময় থাকে তবে আমি দ্বিতীয় চেহারাটি দেখতে যাব এবং এটি দেখতে কিভাবে এটি বর্তমান উত্তরের সাথে তুলনা করে।
মাইকেল মূল্য

@ মিশেলপ্রাইস - নিখুঁতভাবে শেখার দৃষ্টিকোণ থেকে আমি এমন কোনও বিকল্প সমাধান দেখতে আগ্রহী যেগুলি স্ট্যাক পয়েন্টার (বা অনুরূপভাবে কনভেনশন নির্দিষ্ট কৌশলগুলি আহ্বান করে) ডুবে থাকা কিছু ভয়ঙ্কর হ্যাকের দিকে না ফোটে।
ফ্লেক্সো

2

সি ++ ১৪ তম স্ট্যান্ড :: সূচি_সৌকর্য (এবং ফাংশন রিটার্ন টাইপ টেমপ্লেট পরামিতি retT হিসাবে) ব্যবহার করে জোহানেস থেকে সমাধানের আমার প্রকরণ:

template <typename RetT, typename ...Args>
struct save_it_for_later
{
    RetT (*func)(Args...);
    std::tuple<Args...> params;

    save_it_for_later(RetT (*f)(Args...), std::tuple<Args...> par) : func { f }, params { par } {}

    RetT delayed_dispatch()
    {
        return callFunc(std::index_sequence_for<Args...>{});
    }

    template<std::size_t... Is>
    RetT callFunc(std::index_sequence<Is...>)
    {
        return func(std::get<Is>(params) ...);
    }
};

double foo(int x, float y, double z)
{
  return x + y + z;
}

int testTuple(void)
{
  std::tuple<int, float, double> t = std::make_tuple(1, 1.2, 5);
  save_it_for_later<double, int, float, double> saved (&foo, t);
  cout << saved.delayed_dispatch() << endl;
  return 0;
}

এই সমস্ত সমাধানগুলি প্রাথমিক সমস্যার সমাধান করতে পারে, তবে সত্যই বলছি, সরলতা এবং রক্ষণাবেক্ষণের দিক থেকে - এই টেমপ্লেটটি কোনও ভুল পথে চলেছে না ?
xy

আমি মনে করি যে টেমপ্লেটগুলি সি ++ 11 এবং 14 এর সাথে আরও ভাল এবং আরও বোধগম্য হয়েছে A কয়েক বছর আগে যখন আমি টুপিগুলির সাহায্যে হুডের নীচে কী উত্সাহ দেয় তা দেখে আমি সত্যিই নিরুৎসাহিত হয়ে পড়েছিলাম। আমি সম্মত হই যে ভাল টেম্পলেটগুলি বিকাশ করা কেবলমাত্র সেগুলি ব্যবহারের চেয়ে উল্লেখযোগ্যভাবে আরও কঠিন।
স্কুয়ার্ট

1
@xy প্রথমত, টেমপ্লেট জটিলতার নিরিখে, এটি কিছুই নয় । দ্বিতীয়ত, বেশিরভাগ সহায়ক টেম্পলেটগুলি পরে তা ইনস্ট্যান্ট করার সময় এক টন সময় সাশ্রয়ের জন্য প্রাথমিক বিনিয়োগ। সর্বশেষে, কি, আপনি বরং হবে না কি টেমপ্লেট আপনাকে যা করতে অনুমতি দিই ক্ষমতা আছে? আপনি কেবল এটি ব্যবহার করতে পারবেন না, এবং অপ্রাসঙ্গিক মন্তব্যগুলি ছেড়ে যান না যা অন্য প্রোগ্রামারদের পুলিশিং করে বলে মনে হচ্ছে।
আন্ডারস্কোর_দি
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.