সি ++ তে জেনেরিক স্ট্রাক্টের তুলনা কীভাবে?


13

আমি জেনেরিক উপায়ে স্ট্রাক্টের তুলনা করতে চাই এবং আমি এরকম কিছু করেছি (আমি প্রকৃত উত্সটি ভাগ করতে পারি না, তাই প্রয়োজনে আরও বিশদ জানতে চাই):

template<typename Data>
bool structCmp(Data data1, Data data2)
{
  void* dataStart1 = (std::uint8_t*)&data1;
  void* dataStart2 = (std::uint8_t*)&data2;
  return memcmp(dataStart1, dataStart2, sizeof(Data)) == 0;
}

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

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

সম্পাদনা: আমি দুর্ভাগ্যক্রমে সি ++ 11 এর সাথে আটকে আছি। এটি আগে উল্লেখ করা উচিত ছিল ...


এই ব্যর্থতা যেখানে আপনি একটি উদাহরণ দেখাতে পারেন? এক ধরণের সমস্ত উদাহরণের জন্য প্যাডিং একই হওয়া উচিত, না?
idclev 463035818

1
@ idclev463035818 প্যাডিং অনির্ধারিত, আপনি এটির মূল্য ধরে নিতে পারবেন না এবং আমি বিশ্বাস করি এটি ইউবি এটি পড়ার চেষ্টা করবে (শেষ অংশে নিশ্চিত নয়)।
ফ্রান্সোইস অ্যান্ডরিয়াক্স

@ idclev463035818 প্যাডিং মেমরির ক্ষেত্রে একই আপেক্ষিক স্থানে রয়েছে তবে এতে আলাদা আলাদা ডেটা থাকতে পারে। এটি স্ট্রাক্টের সাধারণ ব্যবহারগুলিতে ফেলে দেওয়া হয় যাতে সংকলক এটি শূন্য করতে বিরক্ত না করে।
NO_NAME

2
@ idclev463035818 প্যাডিংয়ের একই আকার রয়েছে। বিডের যে প্যাডিংগুলি গঠন করে তা যে কোনও কিছু হতে পারে। আপনি যখন এই memcmpতুলনায় বিভাজন বিট অন্তর্ভুক্ত।
ফ্রান্সোইস অ্যান্ডরিয়াক্স

1
আমি ইয়্যাকিসারভিনেনের সাথে একমত ... ক্লাস ব্যবহার করুন, স্ট্রাক্ট নয়, এবং ==অপারেটরটি বাস্তবায়ন করুন । ব্যবহার memcmpঅবিশ্বাস্য, এবং খুব শীঘ্রই বা আপনারা এমন কিছু শ্রেণীর সাথে কাজ করছেন যা "অন্যদের থেকে কিছুটা আলাদাভাবে করতে হবে"। এটি একটি অপারেটরে প্রয়োগ করার জন্য এটি খুব পরিষ্কার এবং দক্ষ। আসল আচরণটি বহুমুখী হবে তবে উত্স কোডটি পরিষ্কার হবে ... এবং, সুস্পষ্ট।
মাইক রবিনসন

উত্তর:


7

না, memcmpএটি করা উপযুক্ত নয়। এবং সি ++ এর প্রতিবিম্ব এই মুহূর্তে এটি করার জন্য অপর্যাপ্ত (পরীক্ষামূলক সংকলকগুলি হতে পারে যা ইতিমধ্যে এটি করার জন্য যথেষ্ট প্রতিফলনকে সমর্থন করে এবং এর মধ্যে আপনার প্রয়োজনীয় বৈশিষ্ট্য থাকতে পারে)।

অন্তর্নির্মিত প্রতিবিম্ব ছাড়াই আপনার সমস্যা সমাধানের সহজতম উপায় হ'ল কিছু ম্যানুয়াল প্রতিবিম্ব।

এটা নাও:

struct some_struct {
  int x;
  double d1, d2;
  char c;
};

আমরা সর্বনিম্ন কাজ করতে চাই যাতে আমরা এর মধ্যে দুটি তুলনা করতে পারি।

যদি আমাদের থাকে:

auto as_tie(some_struct const& s){ 
  return std::tie( s.x, s.d1, s.d2, s.c );
}

অথবা

auto as_tie(some_struct const& s)
-> decltype(std::tie( s.x, s.d1, s.d2, s.c ))
{
  return std::tie( s.x, s.d1, s.d2, s.c );
}

জন্য , তারপর:

template<class S>
bool are_equal( S const& lhs, S const& rhs ) {
  return as_tie(lhs) == as_tie(rhs);
}

একটি সুন্দর শালীন কাজ করে।

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

এর জন্য প্রতি সদস্যের "প্রতিচ্ছবি" ডেটা লেখার সাথে সাথে কিছুটা লাইব্রেরি (কোডের 100ish লাইন?) প্রয়োজন হবে। আপনার কাছে থাকা স্ট্রকের সংখ্যা যদি সীমাবদ্ধ থাকে তবে প্রতি স্ট্রাক্ট কোডটি ম্যানুয়ালি লেখা সহজ হতে পারে।


পাবার উপায় সম্ভবত আছে

REFLECT( some_struct, x, d1, d2, c )

as_tieভয়ঙ্কর ম্যাক্রো ব্যবহার করে কাঠামো তৈরি করতে । তবে as_tieযথেষ্ট সহজ। ইন পুনরাবৃত্তি বিরক্তিকর; এটি দরকারী:

#define RETURNS(...) \
  noexcept(noexcept(__VA_ARGS__)) \
  -> decltype(__VA_ARGS__) \
  { return __VA_ARGS__; }

এই পরিস্থিতিতে এবং অনেক অন্যান্য। সহ RETURNS, লেখাটি as_tieহ'ল:

auto as_tie(some_struct const& s)
  RETURNS( std::tie( s.x, s.d1, s.d2, s.c ) )

পুনরাবৃত্তি অপসারণ।


এটি পুনরাবৃত্তি করার জন্য এখানে ছুরিকাঘাত:

template<class T,
  typename std::enable_if< !std::is_class<T>{}, bool>::type = true
>
auto refl_tie( T const& t )
  RETURNS(std::tie(t))

template<class...Ts,
  typename std::enable_if< (sizeof...(Ts) > 1), bool>::type = true
>
auto refl_tie( Ts const&... ts )
  RETURNS(std::make_tuple(refl_tie(ts)...))

template<class T, std::size_t N>
auto refl_tie( T const(&t)[N] ) {
  // lots of work in C++11 to support this case, todo.
  // in C++17 I could just make a tie of each of the N elements of the array?

  // in C++11 I might write a custom struct that supports an array
  // reference/pointer of fixed size and implements =, ==, !=, <, etc.
}

struct foo {
  int x;
};
struct bar {
  foo f1, f2;
};
auto refl_tie( foo const& s )
  RETURNS( refl_tie( s.x ) )
auto refl_tie( bar const& s )
  RETURNS( refl_tie( s.f1, s.f2 ) )

ref রেফেল_টি (অ্যারে) (সম্পূর্ণ পুনরাবৃত্তি, এমনকি অ্যারে অফ-অ্যারে সমর্থন করে):

template<class T, std::size_t N, std::size_t...Is>
auto array_refl( T const(&t)[N], std::index_sequence<Is...> )
  RETURNS( std::array<decltype( refl_tie(t[0]) ), N>{ refl_tie( t[Is] )... } )

template<class T, std::size_t N>
auto refl_tie( T(&t)[N] )
  RETURNS( array_refl( t, std::make_index_sequence<N>{} ) )

সরাসরি উদাহরণ

এখানে আমি একটি ব্যবহার std::arrayএর refl_tie। এটি সংকলনের সময়ে আমার পূর্ববর্তী রেফিলার তুলনায় অনেক দ্রুত।

এছাড়াও

template<class T,
  typename std::enable_if< !std::is_class<T>{}, bool>::type = true
>
auto refl_tie( T const& t )
  RETURNS(std::cref(t))

ব্যবহার std::crefপরিবর্তে এখানে std::tieকম্পাইল-টাইম ওভারহেড উপর বাঁচাতে পারে, যেমন crefতুলনায় অনেক সহজ ক্লাস হয় tuple

অবশেষে, আপনি যোগ করা উচিত

template<class T, std::size_t N, class...Ts>
auto refl_tie( T(&t)[N], Ts&&... ) = delete;

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

এটি ব্যতীত, আপনি যদি কোনও অ-প্রতিবিম্বিত কাঠামোতে অ্যারে পাস করেন তবে এটি পয়েন্টার-থেকে-অপ-প্রতিবিম্বিত কাঠামোর উপরে refl_tieফিরে আসে, যা কাজ করে এবং বাজে কথা দেয়।

এটির সাহায্যে আপনি একটি সংকলন-সময় ত্রুটি শেষ করেন।


লাইব্রেরি ধরণের মাধ্যমে পুনরাবৃত্তি জন্য সমর্থন জটিল। আপনি std::tieতাদের করতে পারেন :

template<class T, class A>
auto refl_tie( std::vector<T, A> const& v )
  RETURNS( std::tie(v) )

তবে এটি এর মাধ্যমে পুনরাবৃত্তি সমর্থন করে না।


আমি ম্যানুয়াল প্রতিবিম্ব সহ এই ধরণের সমাধান অনুসরণ করতে চাই। আপনার প্রদত্ত কোডটি C ++ 11 এর সাথে কাজ করছে বলে মনে হচ্ছে না। কোন সুযোগ আপনি যে আমাকে সাহায্য করতে পারেন?
ফ্রেডরিক এনেটার্প

1
সি ++ 11 এ এটি কাজ না করার কারণ হ'ল পিছনে ফিরে আসা টাইপের অভাব as_tie। সি ++ 14 থেকে শুরু করে এটি স্বয়ংক্রিয়ভাবে ছাড় হয়। আপনি auto as_tie (some_struct const & s) -> decltype(std::tie(s.x, s.d1, s.d2, s.c));সি ++ 11 এ ব্যবহার করতে পারেন । অথবা স্পষ্টতই রিটার্নের ধরণটি বর্ণনা করুন।
দারহুক

1
@ ফ্রেড্রিকএনেটর্প ফিক্সড, প্লাস এমন ম্যাক্রো যা এটি লিখতে সহজ করে তোলে। এটি সম্পূর্ণরূপে পুনরাবৃত্তভাবে কাজ করার জন্য কাজ (সুতরাং একটি স্ট্রাক্ট-অফ-স্ট্রাক্ট, যেখানে সাবস্ট্রাক্টসের as_tieসমর্থন রয়েছে, স্বয়ংক্রিয়ভাবে কাজ করে) এবং অ্যারের সদস্যদের সমর্থন করা বিশদ নয়, তবে এটি সম্ভব।
ইয়াক্ক - অ্যাডাম নেভ্রামামন্ট

ধন্যবাদ. আমি ভয়ঙ্কর ম্যাক্রোগুলি কিছুটা ভিন্নভাবে করেছি, তবে কার্যত সমতুল্য। আরও একটি সমস্যা। আমি একটি পৃথক শিরোলেখ ফাইলটিতে তুলনাটি সাধারণ করার চেষ্টা করছি এবং এটি বিভিন্ন জিএমক পরীক্ষার ফাইলগুলিতে অন্তর্ভুক্ত করব। এটি ত্রুটির বার্তায় ফলাফল দেয়: একাধিক সংজ্ঞা `as_tie (টেস্ট 1 কনস্ট্যান্ড) 'আমি সেগুলিকে ইনলাইন করার চেষ্টা করছি তবে এটি কাজ করতে পারে না।
ফ্রেডরিক এনেটার্প

1
@ ফ্রেড্রিকএনেটর্প inlineকীওয়ার্ডটি একাধিক সংজ্ঞা ত্রুটিগুলি দূরে চলে যাওয়া উচিত। আপনি সর্বনিম্ন প্রজননযোগ্য উদাহরণ
ইয়াক্ক - অ্যাডাম নেভ্রামামন্ট

7

আপনি ঠিক বলেছেন যে এইভাবে স্বেচ্ছাসেবীর ধরণের তুলনা করার জন্য প্যাডিং আপনার পথে চলে।

আপনি নিতে পারেন এমন ব্যবস্থা রয়েছে:

  • আপনি যদি নিয়ন্ত্রণে থাকেন Dataতবে জিসিসি রয়েছে __attribute__((packed))। এটির পারফরম্যান্সে প্রভাব রয়েছে, তবে এটি চেষ্টা করে দেখার মতো হতে পারে। যদিও, আমাকে স্বীকার করতে হবে যে packedআপনাকে প্যাডিং সম্পূর্ণরূপে অস্বীকার করতে সক্ষম করে কিনা তা আমি জানি না । জিসিসি ডক বলেছেন:

স্ট্রাক্ট বা ইউনিয়ন ধরণের সংজ্ঞায় সংযুক্ত এই বৈশিষ্ট্যটি নির্দিষ্ট করে যে কাঠামো বা ইউনিয়নের প্রতিটি সদস্য প্রয়োজনীয় মেমরি হ্রাস করার জন্য স্থাপন করা হয়েছে। যখন একটি এনাম সংজ্ঞা সংযুক্ত থাকে, এটি নির্দেশ করে যে ক্ষুদ্রতম ইন্টিগ্রাল প্রকারটি ব্যবহার করা উচিত।

  • আপনি যদি নিয়ন্ত্রণে না থাকেন Dataতবে কমপক্ষে std::has_unique_object_representations<T>আপনাকে বলতে পারেন যে আপনার তুলনাটি সঠিক ফল পাবে:

যদি টি ট্রিভিয়াললিপিযোগ্য হয় এবং যদি একই মানযুক্ত টি টাইপের কোনও দুটি বস্তুর একই বস্তুর উপস্থাপনা থাকে তবে সদস্যের ধ্রুবক মানটি সমান সত্য প্রদান করে। অন্য যে কোনও ধরণের জন্য মানটি মিথ্যা।

এবং আরও:

এই বৈশিষ্ট্যটি বাইট অ্যারে হিসাবে তার অবজেক্টের উপস্থাপনা হ্যাশ করে কোনও ধরণের সঠিকভাবে হ্যাশ করা যায় কিনা তা নির্ধারণের জন্য এটি চালু করা হয়েছিল।

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


1
আমি এই উত্তর পছন্দ। এই ধরণের বৈশিষ্ট্য সহ, আপনি memcmpকোনও প্যাডিং ছাড়াই স্ট্রাক্টগুলিতে ব্যবহার করতে SFINAE ব্যবহার করতে পারেন এবং operator==প্রয়োজন হলে কেবল প্রয়োগ করতে পারেন ।
ইয়াকিসারভিনেন

ঠিক আছে ধন্যবাদ. এটির সাহায্যে আমি নিরাপদে সিদ্ধান্ত নিতে পারি যে আমাকে কিছু ম্যানুয়াল প্রতিচ্ছবি করতে হবে।
ফ্রেডরিক এনেটার্প

6

সংক্ষেপে: জেনেরিক উপায়ে সম্ভব নয়।

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

এটি বাস্তবায়নের সর্বোত্তম উপায় হ'ল সকল সদস্যের তুলনা করা। অবশ্যই এটি জেনেরিক উপায়ে সত্যিই সম্ভব নয় (যতক্ষণ না আমরা সি ++ ২৩ বা তার পরে সংকলিত সময় প্রতিচ্ছবি এবং মেটা ক্লাসগুলি পাই)। সি ++ ২০ এর পরে, কেউ একটি ডিফল্ট উত্পন্ন করতে পারে operator<=>তবে আমি মনে করি এটি কেবল সদস্য ফাংশন হিসাবেই সম্ভব হবে, আবার এটি সত্যিই প্রযোজ্য নয়। আপনি যদি ভাগ্যবান এবং সমস্ত স্ট্রাক্টের সাথে তুলনা করতে চান তার একটি operator==সংজ্ঞা আছে, আপনি অবশ্যই এটি ব্যবহার করতে পারেন। তবে এটির গ্যারান্টি নেই।

সম্পাদনা: ঠিক আছে, সমষ্টিগুলির জন্য আসলে একটি সম্পূর্ণ হ্যাকি এবং কিছুটা জেনেরিক উপায়। (আমি কেবল টিউপসগুলিতে রূপান্তরটি লিখেছি, যাদের ডিফল্ট তুলনা অপারেটর রয়েছে)। godbolt


দুর্দান্ত হ্যাক! দুর্ভাগ্যক্রমে, আমি সি ++ 11 এর সাথে আটকে আছি তাই আমি এটি ব্যবহার করতে পারি না।
ফ্রেডরিক এনেটার্প

2

সি ++ 20 ডিফল্ট কমপ্যারিসনকে সমর্থন করে

#include <iostream>
#include <compare>

struct XYZ
{
    int x;
    char y;
    long z;

    auto operator<=>(const XYZ&) const = default;
};

int main()
{
    XYZ obj1 = {4,5,6};
    XYZ obj2 = {4,5,6};

    if (obj1 == obj2)
    {
        std::cout << "objects are identical\n";
    }
    else
    {
        std::cout << "objects are not identical\n";
    }
    return 0;
}

1
যদিও এটি একটি খুব দরকারী বৈশিষ্ট্য, এটি জিজ্ঞাসা করা প্রশ্নের উত্তর দেয় না। ওপি বলেছিল যে "আমি ব্যবহৃত স্ট্রাক্টগুলি সংশোধন করতে পারছি না", যার অর্থ সি ++ ২০ ডিফল্ট সমতা অপারেটর উপলব্ধ থাকলেও ওপি কেবলমাত্র ডিফল্ট হওয়ার পরে ==বা <=>অপারেটরগুলিই কাজটি করতে পারে ক্লাস স্কোপ এ।
নিকল বোলাস

যেমন নিকোল বোলাস বলেছিলেন, আমি স্ট্রাইকগুলি সংশোধন করতে পারি না।
ফ্রেডরিক এনেটার্প

1

পিওডি ডেটা ধরে নিচ্ছি, ডিফল্ট অ্যাসাইনমেন্ট অপারেটর কেবল সদস্য বাইটগুলি অনুলিপি করে। (আসলে এটি সম্পর্কে 100% নিশ্চিত নয়, এর জন্য আমার শব্দটি গ্রহণ করবেন না)

আপনি আপনার সুবিধার্থে এটি ব্যবহার করতে পারেন:

template<typename Data>
bool structCmp(Data data1, Data data2) // Data is POD
{
  Data tmp;
  memcpy(&tmp, &data1, sizeof(Data)); // copy data1 including padding
  tmp = data2;                        // copy data2 only members
  return memcmp(&tmp, &data1, sizeof(Data)) == 0; 
}

@ ওয়ালনাট আপনি ঠিক বলেছেন যে এক ভয়ানক উত্তর ছিল। একটি আবার লিখেছেন।
কোস্টাস

স্ট্যান্ডার্ড গ্যারান্টি দেয় যে অ্যাসাইনমেন্টটি প্যাডিং বাইটগুলি ছোঁয়াছে? মৌলিক ধরণের একই মানের জন্য একাধিক অবজেক্টের উপস্থাপনা সম্পর্কে এখনও উদ্বেগ রয়েছে।
আখরোট

@walnut আমি বিশ্বাস করি এটা আছে
কোস্টাস

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

আমি এখন এটি পরীক্ষা করেছি এবং এটি কাজ করে না। অ্যাসাইনমেন্ট প্যাডিং বাইটগুলি ছোঁয়া ছেড়ে দেয় না।
ফ্রেডরিক এনেটার্প

0

আমি বিশ্বাস করি যে আপনি অ্যান্টনি পোলখিনের আশ্চর্যজনকভাবে কৃপণ ভুডোটি magic_getলাইব্রেরিতে সমাধান করতে সক্ষম হবেন - জটিল শ্রেণীর জন্য নয়, স্ট্রাইকগুলির জন্য।

সেই লাইব্রেরির সাহায্যে আমরা কোনও কাঠামোর বিভিন্ন ক্ষেত্রকে তাদের উপযুক্ত প্রকারের সাথে খাঁটি-সাধারণ-টেম্প্লেটেড কোডে পুনরাবৃত্তি করতে সক্ষম হয়েছি। অ্যান্টনি এটি ব্যবহার করেছে, উদাহরণস্বরূপ, সম্পূর্ণ উদারভাবে সঠিক প্রকারের সাথে নির্বিচারে স্ট্রাইকগুলি একটি আউটপুট প্রবাহে প্রবাহিত করতে সক্ষম হতে। এটি তুলনামূলক এই পদ্ধতির সম্ভাব্য প্রয়োগ হতে পারে যে যুক্তি দাঁড়ায়।

... তবে আপনার সি ++ 14 লাগবে। কমপক্ষে এটি সি ++ 17 এর চেয়ে ভাল এবং অন্যান্য উত্তরগুলির পরবর্তী পরামর্শগুলি :- পি

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