আমি অনেকগুলি সি প্রোগ্রামারকে দেখি যা সি ++ কে ঘৃণা করে। কোনটি ভাল এবং এটি সম্পর্কে মন্দ কী তা ধীরে ধীরে বুঝতে আমার বেশ কিছু সময় (বছর) সময় লেগেছে। আমি মনে করি এটি উচ্চারণের সর্বোত্তম উপায় এটি:
কম কোড, রান-টাইম ওভারহেড নেই, আরও সুরক্ষা।
আমরা যত কম কোড লিখি তত ভাল। শ্রেষ্ঠত্বের জন্য প্রচেষ্টা করা সমস্ত প্রকৌশলে এটি দ্রুত পরিষ্কার হয়ে যায়। আপনি এক জায়গায় একটি বাগ ঠিক করেছেন, অনেকগুলি নয় - আপনি একবার একটি অ্যালগরিদম প্রকাশ করেন এবং এটি আবার অনেক জায়গায় ব্যবহার করেন Gree গ্রীকদের এমনকি একটি বক্তব্য রয়েছে, প্রাচীন স্পার্টানদের কাছে পাওয়া যায়: "কম শব্দে কিছু বলতে, মানে যে আপনি এটি সম্পর্কে জ্ঞানী "। এবং বিষয়টির সত্যতাটি হ'ল, যখন সঠিকভাবে ব্যবহার করা হয় , তখন সি ++ আপনাকে সি এর চেয়ে অনেক কম কোডে নিজেকে রানটাইম গতির জন্য ব্যয় না করে নিরাপদ (অর্থাৎ সংকলন-সময়ে আরও ত্রুটি ধরা) সি এর চেয়ে বেশি প্রকাশ করতে দেয়।
এখানে আমার রেন্ডারারের একটি সরলীকৃত উদাহরণ রয়েছে : যখন ত্রিভুজের স্ক্যানলাইন জুড়ে পিক্সেল মানগুলি ইন্টারপোল্ট করা হয়। আমাকে একটি X স্থানাঙ্ক x1 থেকে শুরু করতে হবে এবং একটি X স্থানাঙ্ক x2 (বাম থেকে একটি ত্রিভুজের ডান দিকে) পৌঁছাতে হবে। এবং প্রতিটি ধাপ জুড়ে, প্রতিটি পিক্সেল জুড়ে আমি পাস করি, আমাকে মানগুলি ফাঁক করতে হবে।
যখন আমি পরিবেষ্টনের আলোকে পিক্সেলে পৌঁছে দেই:
typedef struct tagPixelDataAmbient {
int x;
float ambientLight;
} PixelDataAmbient;
...
// inner loop
currentPixel.ambientLight += dv;
যখন আমি রঙটি বিভক্ত করি ("গৌরুদ" শেডিং বলা হয়, যেখানে "লাল", "সবুজ" এবং "নীল" ক্ষেত্রগুলি প্রতিটি পিক্সেলের একটি ধাপের মান দ্বারা বিভক্ত হয়):
typedef struct tagPixelDataGouraud {
int x;
float red;
float green;
float blue; // The RGB color interpolated per pixel
} PixelDataGouraud;
...
// inner loop
currentPixel.red += dred;
currentPixel.green += dgreen;
currentPixel.blue += dblue;
আমি যখন "ফোং" শেডিংয়ের রেন্ডার করি তখন আমি আর তীব্রতা (অ্যাম্বিয়েন্টলাইট) বা কোনও রঙ (লাল / সবুজ / নীল) ইন্টারপোলেট করি না - আমি একটি সাধারণ ভেক্টরকে (এনএক্স, এনওয়াই, এনজেড) ইন্টারপোল্ট করি এবং প্রতিটি পদক্ষেপে আমাকে পুনরায় ফিরে আসতে হয় আন্তঃবিবাহিত সাধারণ ভেক্টরের উপর ভিত্তি করে আলো সমীকরণের গণনা করুন:
typedef struct tagPixelDataPhong {
int x;
float nX;
float nY;
float nZ; // The normal vector interpolated per pixel
} PixelDataPhong;
...
// inner loop
currentPixel.nX += dx;
currentPixel.nY += dy;
currentPixel.nZ += dz;
এখন, সি প্রোগ্রামারগুলির প্রথম প্রবৃত্তিটি হ্যাক হবে "মান, তিনটি ফাংশন লিখুন যা মানগুলি বিভক্ত করে এবং সেট মোডের উপর নির্ভর করে তাদের কল করে"। প্রথমত, এর অর্থ এই যে আমার একটি টাইপ সমস্যা আছে - আমি কী নিয়ে কাজ করব? আমার পিক্সেলগুলি পিক্সেলডেটা এম্বিয়েন্ট? PixelDataGouraud? PixelDataPhong? ওহ, অপেক্ষা করুন, দক্ষ সি প্রোগ্রামার বলেছেন, ইউনিয়ন ব্যবহার করুন!
typedef union tagSuperPixel {
PixelDataAmbient a;
PixelDataGouraud g;
PixelDataPhong p;
} SuperPixel;
.. এবং তারপর, আপনার একটি ফাংশন আছে ...
RasterizeTriangleScanline(
enum mode, // { ambient, gouraud, phong }
SuperPixel left,
SuperPixel right)
{
int i,j;
if (mode == ambient) {
// handle pixels as ambient...
int steps = right.a.x - left.a.x;
float dv = (right.a.ambientLight - left.a.ambientLight)/steps;
float currentIntensity = left.a.ambientLight;
for (i=left.a.x; i<right.a.x; i++) {
WorkOnPixelAmbient(i, dv);
currentIntensity+=dv;
}
} else if (mode == gouraud) {
// handle pixels as gouraud...
int steps = right.g.x - left.g.x;
float dred = (right.g.red - left.g.red)/steps;
float dgreen = (right.g.green - left.a.green)/steps;
float dblue = (right.g.blue - left.g.blue)/steps;
float currentRed = left.g.red;
float currentGreen = left.g.green;
float currentBlue = left.g.blue;
for (j=left.g.x; i<right.g.x; j++) {
WorkOnPixelGouraud(j, currentRed, currentBlue, currentGreen);
currentRed+=dred;
currentGreen+=dgreen;
currentBlue+=dblue;
}
...
আপনি বিশৃঙ্খলা পিছলে পিছনে অনুভব করেন?
প্রথমত, আমার কোডটি ক্র্যাশ করার জন্য একটি টাইপো হ'ল প্রয়োজনীয়, যেহেতু সংকলক আমাকে কখনই ফাংশনের "গৌরুদ" বিভাগে থামাতে পারবে না, আসলে ".এ" অ্যাক্সেস করতে। (পরিবেষ্টিত) মান। সি টাইপ সিস্টেমে (যেটি সংকলনের সময়) ধরা পড়ে না এমন বাগের অর্থ, এমন একটি বাগ যা রান-টাইমে প্রকাশ পায় এবং এটির জন্য ডিবাগিংয়ের প্রয়োজন হবে। আপনি কি লক্ষ্য করেছেন যে আমি left.a.green
"ডিজিগ্রিন" গণনায় প্রবেশ করছি ? সংকলক নিশ্চয়ই আপনাকে তা বলেন নি।
তারপরে, সর্বত্র পুনরাবৃত্তি রয়েছে - for
লেন্ডারটি যতবার রেন্ডারিং মোড রয়েছে ততবার রয়েছে, আমরা "ডান বিয়োগ বামে ধাপে বিভক্ত" করে চলেছি। কুরুচিপূর্ণ, এবং ত্রুটি-প্রবণ। আপনি কি লক্ষ্য করেছেন যে গৌড় লুপে "আমি" ব্যবহার করার সাথে আমি তুলনা করেছি, যখন আমার "জ" ব্যবহার করা উচিত ছিল? সংকলক আবার, নীরব।
মোডগুলির জন্য / অন্য / মই সম্পর্কে কী? আমি যদি তিন সপ্তাহের মধ্যে একটি নতুন রেন্ডারিং মোড যুক্ত করি তবে কী হবে? আমার সমস্ত কোডে "if মোড ==" সমস্ত নতুন মোডটি হ্যান্ডেল করা কি মনে আছে?
সি ++ স্ট্রাক্টের এই সেট এবং একটি টেম্পলেট ফাংশনটির সাথে উপরের কদর্যতা তুলনা করুন:
struct CommonPixelData {
int x;
};
struct AmbientPixelData : CommonPixelData {
float ambientLight;
};
struct GouraudPixelData : CommonPixelData {
float red;
float green;
float blue; // The RGB color interpolated per pixel
};
struct PhongPixelData : CommonPixelData {
float nX;
float nY;
float nZ; // The normal vector interpolated per pixel
};
template <class PixelData>
RasterizeTriangleScanline(
PixelData left,
PixelData right)
{
PixelData interpolated = left;
PixelData step = right;
step -= left;
step /= int(right.x - left.x); // divide by pixel span
for(int i=left.x; i<right.x; i++) {
WorkOnPixel<PixelData>(interpolated);
interpolated += step;
}
}
এখন এই দেখুন। আমরা আর ইউনিয়ন টাইপ-স্যুপ তৈরি করি না: প্রতিটি মোডে আমাদের নির্দিষ্ট প্রকার রয়েছে। একটি বেস শ্রেণি ( CommonPixelData
) থেকে উত্তরাধিকার সূত্রে তারা তাদের সাধারণ জিনিসগুলি ("x" ক্ষেত্র) পুনরায় ব্যবহার করে । এবং টেম্পলেটটি সংকলকটি তৈরি করে (যা কোড তৈরি করা) তিনটি আলাদা ফাংশন যা আমরা সিতে লিখতাম, কিন্তু একই সাথে, প্রকারগুলি সম্পর্কে খুব কঠোর হয়ে ওঠে!
টেমপ্লেটের আমাদের লুপটি অজানা ক্ষেত্রগুলিকে অজানা ও অ্যাক্সেস করতে পারে না - আমরা যদি করি তবে সংকলকটি বার্ক করবে।
টেমপ্লেটটি সাধারণ কাজ সম্পাদন করে (লুপ, প্রতিটি সময়ে "ধাপে" বাড়িয়ে) এবং এটি এমনভাবে করতে পারে যাতে রানটাইম ত্রুটিগুলি কেবল তৈরি করতে পারে না। টাইপ প্রতি ক্ষেপক ( AmbientPixelData
, GouraudPixelData
, PhongPixelData
) সঙ্গে সম্পন্ন করা হয় operator+=()
যে আমরা structs মধ্যে যোগ করা হবে - যা মূলত নির্দেশ কিভাবে প্রতিটি টাইপ ইন্টারপোলেট করা হয়।
এবং আপনি কী দেখতে পান যে আমরা ওয়ার্কঅনপিক্সেল <T> দিয়ে কি করেছি? আমরা টাইপ প্রতি বিভিন্ন কাজ করতে চান? আমরা কেবল একটি টেমপ্লেট বিশেষায়িত কল:
void WorkOnPixel<AmbientPixelData>(AmbientPixelData& p)
{
// use the p.ambientLight field
}
void WorkOnPixel<GouraudPixelData>(GouraudPixelData& p)
{
// use the p.red/green/blue fields
}
এটি - কল করার জন্য ফাংশনটি টাইপের উপর নির্ভর করে সিদ্ধান্ত নেওয়া হয়। সংকলন সময়ে!
এটি আবার নতুন করে বলার জন্য:
- আমরা কোডটি ছোট করে (টেমপ্লেটের মাধ্যমে), সাধারণ অংশগুলি পুনরায় ব্যবহার করে,
- আমরা কুৎসিত হ্যাকগুলি ব্যবহার করি না, আমরা একটি কঠোর প্রকারের ব্যবস্থা রাখি, যাতে সংকলকটি আমাদের সর্বদা পরীক্ষা করতে পারে।
- এবং সর্বোত্তম: আমাদের যা কিছুই করা হয়নি তার কোনওটিতেই রানটাইম প্রভাব নেই। এই কোডটি সমতুল্য সি কোডের মতো দ্রুতগতিতে চলবে - আসলে, যদি সি কোডটি বিভিন্ন
WorkOnPixel
সংস্করণে কল করতে ফাংশন পয়েন্টার ব্যবহার করে , তবে সি ++ কোডটি সি-এর চেয়ে দ্রুততর হবে, কারণ সংকলকটি টাইপ-নির্দিষ্ট WorkOnPixel
টেম্পলেট বিশেষায়িতকরণকে ইনলাইন করবে because কল!
কম কোড, রান-টাইম ওভারহেড নেই, আরও সুরক্ষা।
এর অর্থ কি এই যে সি ++ হ'ল সমস্ত ভাষা এবং শেষের ভাষা? অবশ্যই না. আপনার এখনও ট্রেড-অফগুলি পরিমাপ করতে হবে। অজ্ঞ লোকেরা যখন বাশ / পার্ল / পাইথন স্ক্রিপ্ট লিখত তখন তাদের সি ++ ব্যবহার করা হবে। ট্রিগার-হ্যাপি সি ++ newbies ভার্চুয়াল একাধিক উত্তরাধিকারের সাথে গভীর নেস্টেড ক্লাস তৈরি করবে আপনি তাদের থামানোর আগে এবং তাদের প্যাকিং প্রেরণের আগে। এটি প্রয়োজনীয় নয় তা বোঝার আগে তারা জটিল বুস্ট মেটা-প্রোগ্রামিং ব্যবহার করবে । তারা এখনও ব্যবহার করবে char*
, strcmp
এবং ম্যাক্রো, পরিবর্তে std::string
ও টেমপ্লেট।
তবে এটি আর কিছুই বলে না ... আপনি কার সাথে কাজ করছেন তা দেখুন। অযোগ্য ব্যবহারকারীদের থেকে আপনাকে রক্ষা করার কোনও ভাষা নেই (না, এমনকি জাভাও নয়)।
অধ্যয়ন এবং সি ++ ব্যবহার চালিয়ে যান - কেবলমাত্র ওভারডাইসাইন করবেন না।