رفتن به مطلب
جامعه‌ی برنامه‌نویسان مُدرن ایران

پرچمداران

  1. فرهاد شیری

    فرهاد شیری

    مدیران مرجع


    • امتیاز

      10

    • تعداد ارسال ها

      98


  2. کامبیز اسدزاده

    کامبیز اسدزاده

    بنیـــان گذار


    • امتیاز

      9

    • تعداد ارسال ها

      292


  3. قاسم رمضانی منش

    قاسم رمضانی منش

    میانجی گر‌ها


    • امتیاز

      7

    • تعداد ارسال ها

      54


  4. Seyyed Ali Mohammadiye

    • امتیاز

      6

    • تعداد ارسال ها

      3



مطالب محبوب

در حال نمایش مطالب دارای بیشترین امتیاز از زمان یکشنبه, 29 اردیبهشت 1398 در همه بخش ها

  1. 4 امتیاز
    با سلام وقت بخیر, در این مطلب میخواهیم در مورد روش کارکرد پیام رسان ها بیشتر بدانیم و با یکدیگر کد یک پیام رسان ساده را پیاده و بررسی کنیم. طرز کار کرد پیام رسان در نظر داشته باشید که هر پیام رسانی که بر ساختار ها پیاده شده باشد از دو قسمت تشکیل شده است. نرم افزار اصلی برای مدیریت درخواست ها (سرور) نرم افزار برای کاربران (کاربر) به نرم افزار اولی سمت SERVER خواهیم گفت و به بعدی سمت CLIENT خواهیم گفت. روم یا تالار گفتگو ما تنها یک اتاق برای گفتگو در نظر میگیریم و هر کاربری که به سرور متصل شود را در همان تالار اضافه خواهیم کرد. تالار های گفتگو صرفا برای تقکیک سازی ارسال و دریافت ها و محدود کردن بازه ی کاربران مورد نظر... (ممکن است یک کاربر در چند اتاق بطور همزمان باشد.) سیستم های پیام رسان پیشرفته تر مانند تلگرام و ... تالار های زیادی را شامل می شوند. (هر کاربر خودش در کانال و گروه های مختلفی عضو است که هر کدام از آنها یک کانال متفاوت محسوب می شوند.) * دقت شود که منظور از کانال صرفا یک اتاق یا تالار گفتگو است و منظور کانال ارتباطی و پروتکل نیست. نرم افزار اصلی نرم افزار اصلی وظیفه دارد تا تمام کاربرانی که وارد تالار شده اند را به یاد داشته باشد و هر لحظه اماده دریافت درخواست هایی از طرف کاربرانش باشد. و پیام هایی را که از کاربران دریافت می کند برای تمامی کاربران دیگر هم ارسال کند که بسته به خلاقیت و نیاز می تواند هر یک از این بخش ها متفاوت طراحی شود. نرم افزار اصلی باید از قبل اجرا شده باشد. تا کاربران دیگر با استفاده از نرم افزار مخصوص به خودشان بتوانند به سرور متصل شوند و ارسال و دریافت داشته باشند. در نظر داشته باشید که اگر در نرم افزار اصلی اختلالی پیش بیایید و متوقف بشوند. قطا برای تمام کاربران مشکل پیش می آید. مگر اینکه از پایگاه های داده ی داخلی استفاده کرده باشند. (با خلاقیت می توان به گونه های متفاوتی چنین ساختاری را پیاده کرد) نرم افزار کاربران این نرم افزار جذاب ترین بخش است چرا تمام قابلیت هایی را که در اختیار کاربر قرار می دهیم را مستقیما طراحی می کنیم. در نظر داشته باشید که هر چیزی که در این نرم افزار طراحی می شود باید در نرم افزار اصلی پشتیبانی شوند... بنابراین اگر این دو بخش توسط دو فرد یا دو گروه مجزا طراحی می شوند آنها باید توسط داکیومنت ها و جلساتی به نظرات مشابه ای رسیده باشند. (اگرچه اینها تخصص و وظیفه ی تحلیلگر سیستم است! نه بطور همزمان وظیفه توسعه دهنده و برنامه نویس نرم افزار) پیاده سازی یک نمونه اکنون در نظر داریم تا با استفاده از ساختار کتابخانه BoostAsio پروژه ای را با نام BoostAsioChat ایجاد کنیم که در آن می خواهیم یک پیام رسان با حداقل ترین امکانات پایه طراحی کنیم که بیشتر جنبه شخصی و تفریحی دارد. زیرا از ساختار های استاندارد و ایمن و کاربری کاملا بدور است! (می توانید خودتان توسعه دهید و آنرا جالب تر بسازید) ساختار نرم افزار اصلی و سرور را به این صورت تعریف می کنیم : typedef deque<message> messageQueue; class participant { public: virtual ~participant() {} virtual void deliver(const message& messageItem) = 0; }; typedef shared_ptr<participant> participantPointer; class room { public: void join(participantPointer participant); void deliver(const message& messageItem); void leave(participantPointer participant); private: messageQueue messageRecents; enum { max = 200 }; set<participantPointer> participants; }; class session : public participant, public enable_shared_from_this<session> { public: session(tcp::socket socket, room& room) : socket(move(socket)), room_(room); void start(); void deliver(const message& messageItem); private: void readHeader(); void readBody(); void write(); tcp::socket socket; room& room_; message messageItem; messageQueue Messages; }; class server { public: server(boost::asio::io_context& io_context, const tcp::endpoint& endpoint) : acceptor(io_context, endpoint); private: void do_accept(); tcp::acceptor acceptor; room room_; }; int main(int argc, char* argv[]); ساختار نرم افزار کاربر را هم به این صورت تعریف می کنیم : typedef deque<message> messageQueue; class client { public: client(boost::asio::io_context& context, const tcp::resolver::results_type& endpoints) : context(context), socket(context); void write(const message& messageItem); void close(); private: void connect(const tcp::resolver::results_type& endpoints); void readHeader(); void readBody(); void write(); boost::asio::io_context& context; tcp::socket socket; message readMessage; messageQueue writeMessage; }; int main(int argc, char* argv[]); در نظر داریم تا در این پروژه از thread ها نیز استفاده کنیم... در مورد این مفهوم ها می توانید بصورت مجزا تحقیق کنید. بنابراین روش کامپایل این پروژه به این صورت خواهد بود : $ g++ client.cpp -lpthread -o client $ g++ Server.cpp -lpthread -o server آزمایش همانطور که گفته شد در ابتدا نرم افزار اصلی و سرور باید اجرا شود. در اینجا ما تمام ارتباطات شبکه را بر روی یک سیستم در شبکه داخلی برقرار خواهیم کرد... پس نگرانی در مورد ساختار های درونی شبکه و آی پی / دی ان اس / دامین نخواهیم داشت. بنابراین ای پی را می توانید ای پی داخلی یا localhost در نظر بگیرید. برای آزمایش پورت فرضی 4000 را در نظر میگیریم و نرم افزار اصلی را روی این پورت اجرا میکنیم : $ ./server 4000 در این مرحله متوجه می شوید که نرم افزار اصلی با موفقیت اجرا شده است و همچنان اجرا مانده است. بله درست است... نرم افزار اصلی هر لحظه باید منتظر دستور کاربران باشد. اگر لحظه ای برای نرم افزار اصلی اختلالی پیش آید نخواهد توانست دستورات کاربران را انجام یا پاسخ دهد. بنابراین این پردازش را قطع نکنید و اجازه دهید تا نرم افزار اصلی اجرا بماند. در محیط دیگری نرم افزار سمت کاربر را نیز اجرا کنید. این نرم افزار را می توانید به تعداد دلخواه وارد کنید. (همانطور که ممکن است 6 نفر همزمان به سرور متصل باشند / یا ممکن است هیچ فردی به سرور متصل نشوند) ابتدا یک کاربری را به سرور با پورت 4000 و شبکه داخلی وصل می کنیم : $ ./client localhost 4000 first user: you can type message here... حال در محیط دیگری با کاربر جدیدی نیز وارد می شویم : $ ./client localhost 4000 second user: you can type message here... در این پروژه نمونه از کاربران نام کاربری یا نام نمی پرسیم.. و صرفا وقتی وارد محیط گفتگو می شوند... یا زمانی که به سرور متصل می شوند منتظر هستیم تا انها پیامی را بنویسند... هر پیامی را که بنویسند به سرور ارسال می شود و سرور وظیفه دارد تا آنرا برای تمام کاربران بفرستد. و این روند درون یک حلقه بی نهایت تکرار می شوند. پس این ارتباط دو طرفه خواهد بود و هم کاربران برای سرور اطلاعات ارسال می کنند و هم سرور برای کاربران اطلاعات ارسال خواهد کرد. در نظر داشته باشید که کاربر اول می تواند پیامی را بنویسد و به کاربران دیگر ارسال شود. ممکن است کاربر سومی اصلا تصمیمی به نوشتن پیام نداشته باشد و صرفا تمایل به خواندن پیام دیگران داشته باشند. و این کاملا اختیاری است. و ما کاربران را اجباری نمیکنیم. اگرچه شما می توانید با خلاقیت خودتان اینها را با متغییر های کمکی و دستورات شرطی پیاده کنید. کد ها برای پیام ها یک ساختار در نظر میگیریم و بصورت مشترک در هر دو نرم افزار استفاده خواهیم کرد... بنابراین اینرا در هدر پیاده خواهیم کرد. هدر پیام : (message.hpp) #ifndef message_HPP #define message_HPP #include <cstdio> #include <cstdlib> #include <cstring> using namespace std; class message { public: enum { headerLength = 4 }; enum { maxBodyLength = 512 }; message() : bodyLength_(0) { } const char* data() const { return data_; } char* data() { return data_; } size_t length() const { return headerLength + bodyLength_; } const char* body() const { return data_ + headerLength; } char* body() { return data_ + headerLength; } size_t bodyLength() const { return bodyLength_; } void bodyLength(size_t new_length) { bodyLength_ = new_length; if(bodyLength_ > maxBodyLength) bodyLength_ = maxBodyLength; } bool decodeHeader() { char header[headerLength + 1] = ""; strncat(header, data_, headerLength); bodyLength_ = atoi(header); if(bodyLength_ > maxBodyLength) { bodyLength_ = 0; return false; } return true; } void encodeHeader() { char header[headerLength + 1] = ""; sprintf(header, "%4d", static_cast<int>(bodyLength_)); memcpy(data_, header, headerLength); } private: char data_[headerLength + maxBodyLength]; size_t bodyLength_; }; #endif نرم افزار اصلی و سرور : (server.cpp) #include <iostream> #include <cstdlib> #include <deque> #include <memory> #include <list> #include <set> #include <utility> #include <boost/asio.hpp> #include "message.hpp" using boost::asio::ip::tcp; using namespace std; typedef deque<message> messageQueue; class participant { public: virtual ~participant() {} virtual void deliver(const message& messageItem) = 0; }; typedef shared_ptr<participant> participantPointer; class room { public: void join(participantPointer participant) { participants.insert(participant); for(auto messageItem: messageRecents) participant->deliver(messageItem); } void deliver(const message& messageItem) { messageRecents.push_back(messageItem); while(messageRecents.size() > max) messageRecents.pop_front(); for(auto participant: participants) participant->deliver(messageItem); } void leave(participantPointer participant) { participants.erase(participant); } private: messageQueue messageRecents; enum { max = 200 }; set<participantPointer> participants; }; class session : public participant, public enable_shared_from_this<session> { public: session(tcp::socket socket, room& room) : socket(move(socket)), room_(room) { } void start() { room_.join(shared_from_this()); readHeader(); } void deliver(const message& messageItem) { bool write_in_progress = !Messages.empty(); Messages.push_back(messageItem); if(!write_in_progress) { write(); } } private: void readHeader() { auto self(shared_from_this()); boost::asio::async_read(socket, boost::asio::buffer(messageItem.data(), message::headerLength), [this, self](boost::system::error_code ec, size_t) { if(!ec && messageItem.decodeHeader()) { readBody(); } else { room_.leave(shared_from_this()); } }); } void readBody() { auto self(shared_from_this()); boost::asio::async_read(socket, boost::asio::buffer(messageItem.body(), messageItem.bodyLength()), [this, self](boost::system::error_code ec, size_t) { if(!ec) { room_.deliver(messageItem); readHeader(); } else { room_.leave(shared_from_this()); } }); } void write() { auto self(shared_from_this()); boost::asio::async_write(socket, boost::asio::buffer(Messages.front().data(), Messages.front().length()), [this, self](boost::system::error_code ec, size_t) { if(!ec) { Messages.pop_front(); if(!Messages.empty()) { write(); } } else { room_.leave(shared_from_this()); } }); } tcp::socket socket; room& room_; message messageItem; messageQueue Messages; }; class server { public: server(boost::asio::io_context& io_context, const tcp::endpoint& endpoint) : acceptor(io_context, endpoint) { do_accept(); } private: void do_accept() { acceptor.async_accept([this](boost::system::error_code ec, tcp::socket socket) { if(!ec) { make_shared<session>(move(socket), room_)->start(); } do_accept(); }); } tcp::acceptor acceptor; room room_; }; int main(int argc, char* argv[]) { try { if(argc < 2) { cerr << "Usage: server <port> [<port> ...]\n"; return 1; } boost::asio::io_context io_context; list<server> servers; for(int i = 1; i < argc; ++i) { tcp::endpoint endpoint(tcp::v4(), atoi(argv[i])); servers.emplace_back(io_context, endpoint); } io_context.run(); } catch (exception& e) { cerr << "Exception: " << e.what() << "\n"; } return 0; } نرم افزار دوم و سمت کاربر : (client.cpp) #include <iostream> #include <thread> #include <cstdlib> #include <deque> #include <boost/asio.hpp> #include "message.hpp" using boost::asio::ip::tcp; using namespace std; typedef deque<message> messageQueue; class client { public: client(boost::asio::io_context& context, const tcp::resolver::results_type& endpoints) : context(context), socket(context) { connect(endpoints); } void write(const message& messageItem) { boost::asio::post(context, [this, messageItem]() { bool write_in_progress = !writeMessage.empty(); writeMessage.push_back(messageItem); if(!write_in_progress) { write(); } }); } void close() { boost::asio::post(context, [this]() { socket.close(); }); } private: void connect(const tcp::resolver::results_type& endpoints) { boost::asio::async_connect(socket, endpoints, [this](boost::system::error_code ec, tcp::endpoint) { if(!ec) { readHeader(); } }); } void readHeader() { boost::asio::async_read(socket, boost::asio::buffer(readMessage.data(), message::headerLength), [this](boost::system::error_code ec, size_t) { if(!ec && readMessage.decodeHeader()) { readBody(); } else { socket.close(); } }); } void readBody() { boost::asio::async_read(socket, boost::asio::buffer(readMessage.body(), readMessage.bodyLength()), [this](boost::system::error_code ec, size_t) { if(!ec) { cout.write(readMessage.body(), readMessage.bodyLength()); cout << "\n"; readHeader(); } else { socket.close(); } }); } void write() { boost::asio::async_write(socket, boost::asio::buffer(writeMessage.front().data(), writeMessage.front().length()), [this](boost::system::error_code ec, size_t) { if(!ec) { writeMessage.pop_front(); if(!writeMessage.empty()) { write(); } } else { socket.close(); } }); } boost::asio::io_context& context; tcp::socket socket; message readMessage; messageQueue writeMessage; }; int main(int argc, char* argv[]) { try { if(argc != 3) { cerr << "Usage: client <host> <port>\n"; return 1; } boost::asio::io_context context; tcp::resolver resolver(context); auto endpoints = resolver.resolve(argv[1], argv[2]); client c(context, endpoints); thread t([&context](){ context.run(); }); char line[message::maxBodyLength + 1]; while(cin.getline(line, message::maxBodyLength + 1)) { message messageItem; messageItem.bodyLength(strlen(line)); memcpy(messageItem.body(), line, messageItem.bodyLength()); messageItem.encodeHeader(); c.write(messageItem); } c.close(); t.join(); } catch (exception& e) { cerr << "Exception: " << e.what() << "\n"; } return 0; } این پروژه آزمایشی بصورت رایگان و اوپن سورس در اینترنت بخصوص اینجا وجود دارد و می توانید آنرا مستقیما بصورت کامل دانلود کنید. با تشکر, سید علی محمدیه
  2. 2 امتیاز
    با سلام همانطور که میدانید زبان سی پلاس پلاس در سالهای اخیر تغییرات شگرف و چشمگیری نسبت به سالهای اولیه پیدایش این زبان داشته است ، به طوریکه کسانی که از استاندارد های قدیمی این زبان استفاده میکنند همگی اذعان دارند که تا چند سال آینده در صورتی که از تکنیک های اضافه شده جدید به این زبان استفاده نشود و آموزش مناسبی برای این تکنیک ها دیده نشود به طور قطع به یقین نمی توان ادعا داشت که زبان سی پلاس پلاس را بلد هستیم و قادر به تولید نرم افزارهایی با قابلیت نگهداری بالاتر و مقایس پذیر تر و با قابلیت استفاده مجدد بالاتر نخواهیم بود. در صورتی که هیچ آشنایی با این سطح از تغییرات در زبان سی پلاس پلاس را ندارید، پیشنهاد میکنم مقاله دوست عزیزم جناب اسدازاده را از این لینک قابلیت‌های ممتاز ++C در نسخه‌های ۱۱ و ۱۴ و ۱۷ حتما مطالعه نمایید. در این آموزش قصد داریم که نحوه استفاده از تکنیک های جدید زبان را باهم استفاده کنیم و در صورتی که دوستان نظری راجع به این مثالها و این آموزش داشتند حتما خوشحال خواهیم شد که به ما در این مهم کمک کنید تا بتوانیم سهمی هرچند کوچک در گسترش بهتر این زبان داشته باشیم. قبلا از تمامی دوستان و اعضای محترمی که این آموزش را مطالعه میکنند و به ما کمک میکنند کمال تشکر و قدر دانی را دارم. استفاده از کلاس std::launder اشاره به آدرس یک زیر شی Sub Object در یک مجموعه، به طور مثال اگر یک struct ویک union به این صورت تعریف کنیم struct X { const int n; }; union U { X x; }; حال اگر یک شی بدین صورت تعرف کنیم... U u = {{ 1 }}; اکنون در صورتی که بخواهید از مقدار عضو n که در X تعریف شده است، توسط شی u دسترسی داشته باشیم بدون اینکه شی جدیدی از X ساخته باشیم به روش زیر عمل خواهیم کرد. X *p = new (&u.x) X {2}; اکنون اشاره گری خواهیم داشت که به یک عضو از U اشاره خواهد داشت، بنابراین نتیجه عبارت ادعایی زیر هم درست خواهد بود. assert(p->n == 2); // OK اکنون اگر عبارتی ادعایی به شکل زیر داشته باشیم قطعا یک undefined Behaviour خواهیم داشت ، بدین علت که احتمال این وجود دارد که نتوان به شی sub Object اشاره داشته باشیم. assert(u.x.n == 2); ودرنهایت در C++17 با استفاده از کلاس std::launder می توانید چنین دسترسی داشته باشید که هم ایمن هست و قطعا قابلیت استفاده مجدد بهتری هم به ارمغان خواهد آورد. assert(*std::launder(&u.x.n) == 2); // OK استفاده از کلاس std::cref , std::ref جهت دسترسی به متغیر های کلاس حافظه ای (Automatic) تعریف شده در یک تابع که به صورت Call By Reference در زمان استفاده از کلاس استاندارد std::function. به فرض مثال، تصور کنید که چند متغیر از نوع int دارید و بطور پیش فرض در زمان تعریف مقادیر اولیه ثابتی هم برای این متغییرها در نظر گرفته ایم و اکنون قصد استفاده از این متغیرها در یک تابع را داریم. اکنون اگر با استفاده از کلاس std::function یک اشاره گر به تابع بسازید و با استفاده از کلاس کمکی std::bind پارامترها مناسب را هم به تابع خود ارسال نمایید، در صورتی که در امضای تابع خود درج کرده باشید که مقادیر ورودی با ارجاع باشند، در زمان فراخوانی تابع شما به مقادیر اولیه تعریف شده در متغییرها دسترسی خواهید داشت. اکنون تصور کنید که بعد از اینکه با استفاده از کلاس std::bind پارامترها را به اشاره گر تابع مقادیر جدید برای متغیرها تعریف کرده باشید، در اینصورت اگر بازهم تابع خود را فراخوانی کنید مشاهده خواهید کرد که فقط به مقادیر اولیه در زمان تعریف متغیر ها دسترسی خواهید داشت و مقادیر جدید متغیرها در دسترس تابع قرار ندارند. اکنون برای رفع این اشکال معمولا دو اه حل وجود دارد... 1- بلافاصله بعد ازتغییر مقادیر متغیرها با استفاده از کلاس std::bind مجددا پارامترها را برای اشاره گر تابع تعریف کنید، که در اینصورت اگر 100 بار مقداردهی مختلف داشته باشید از نظر منطقی و بازدهی کار درستی هست که 100 بار از std::bind استفاده کنید؟ 2- از کلاس های std::ref برای داده های غیر ثابت و std::cref برای داده های ثابت می توانید استفاده کنید. اکنون همانطور که در مثال زیر مشاهده میکنید، در زمان تعریف متغیرهای عددی مقداری برای آنها تعریف کرده ایم و بعد با استفاده از std::bind , std::function یک اشاره گر به تابع ساخته ایم که از سه پارامتر این تابع یک پارامتر رابا استفاده از رفرنس معمولی ارسال کرده ایم و دو پارامتر را با استفاده از کلاسهای std::ref , std::cref ارسال کرده ایم، ودر مرحله بعد مقادیر متغیرها را تغییر داده ایم همانطور که انتظار داشتیم در بدنه تابع تعریف شده مقدار متغیر n با مقدارپیش فرض در دسترس خواهد بود، و قطعا تغییراتی که بر روی این متغیر لحاظ شود را نخواهد دید، نکته ای که در دوپارامتر بعدی مشاهده نمی شود. بنابراین در صورتی که نیاز به تغییر مقادیر متغیر های ارسالی به توابع را در خارج از حوزه دید تابع مورد نظر خود را دارید، و حتما هم لازم هست که تابع شما هم این تغییرات را در دسترس خود داشته باشد باید از کلاس های std::ref , std::cref استفاده نمایید. #include <functional> #include <iostream> void f(int& n1, int& n2, const int& n3) { std::cout << "In function: " << n1 << ' ' << n2 << ' ' << n3 << '\n'; ++n1; ++n2; // ++n3; } int main() { int n1 = 1, n2 = 2, n3 = 3; std::function<void()> bound_f = std::bind(f, n1, std::ref(n2), std::cref(n3)); n1 = 10; n2 = 11; n3 = 12; std::cout << "Before function: " << n1 << ' ' << n2 << ' ' << n3 << '\n'; bound_f(); std::cout << "After function: " << n1 << ' ' << n2 << ' ' << n3 << '\n' << '\n'; n1 = 13; n2 = 14; n3 = 15; std::cout << "Before function: " << n1 << ' ' << n2 << ' ' << n3 << '\n'; bound_f(); std::cout << "After function: " << n1 << ' ' << n2 << ' ' << n3 << '\n'; return 0; } این آموزش ادامه خواهد داشت....
  3. 2 امتیاز
    بله می توانید در سه نخ جداگانه اجرا کنید! چون شما در حقیقت سه تا سوکت ایجاد میکنید که به یک آدرس متصل میشود پس می توانید در نخ های مختلف این عملیات را انجام دهید.! بله می توانید درنخ های مختلف اجرا کنید! منتهی حتما باید از یک قفل جهت کنترل accept استفاده کنید تا race condition رخ ندهد. بله می توانید از AcceptSock1 در دونخ مجزا استفاده کنید به شرط اینکه تمامی شرایط داده ناحیه اشتراکی را در نخ ها لحاظ کنید. در صورتی که ناحیه اشتراکی به درستی تعریف نشده باشد قطعا Race Condition و بعد هم Dead Lock رخ خواهد داد. بله میتوانید اگر شرایط ذکر کرده در بالا را رعایت کنید. بله امکان خواندن از یک سوکت هم در دونخ مجزا هم وجود دارد ولی قطعا پیچیدگی های زیادی برای داده های اشتراکی خواهید داشت. بله ولی قطعا بدون استفاده از قفل گذاری مناسب و ناحیه اشتراکی خیر! در پایان باید عرض کنم که استفاده از نخ ها در سیستم عامل ویندوز با استفاده از کلاس Thread می باشد. که البته کتابخانه های زیادی برای Concurrency , Parallel Prog وجود دارد ولی تقریبا نحوه استفاده از قفلها در ویندوز و لینوکس مشابه هستند. و در لینوکس با استفاده از کلاس pthread می توانید از نخ ها استفاده کنید. البته توجه داشته باشید که ساختمان نخ ها در هر دو سیستم عامل خیلی باهم متفاوت هستند هرچند برنامه نویس خیلی درگیر این پیچیدگی ها نیست چون خود سیستم عامل مدیریت میکنه. بنابراین برای استفاده از سوکت ها در نخ ها ویا حوضچه های نخ حتما باید قوانین نواحی اشتراکی را مد نظر قرار دهید تا هم از بازدهی هرچه بهتر پردازشگر استفاده کنید وهم برنامه ای به مراتب سریعتر از برنامه های ترتیبی ایجاد کرده باشید. ولی فکر کنم ذکر یک نکته خالی از لطف نباشد: تنها راه ایمن ماندن از خطاها و گرفتاری های نخ ها (استفاده نکردن از نخ هاست.) یعنی اگر در استفاده از نخ ها دچار افراط شوید قطعا برنامه هایی به مراتب کندتر از نسخه های ترتیبی خواهید ساخت.
  4. 2 امتیاز
    سلام وقت بخیر, گیت هاب پیج گیتهاب پیج یک سرویس میزبانی وب است که توسط Github اراعه شده است که با استفاده از آن می توانید حتی صفحاتی Static را در پلتفرم وب میزبانی کنید و حتی ابزار هایی مانند Jekyll را می توانید روی این بستر (Github Pages) پیاده و اجرا کنید که در قسمت های آینده در مورد آن توضیح خواهم داد. میزبانی وب سایت هایی که بر پایه Github Pages هستند بصورت رایگان هست و دیگر نیازی به دامین, هاست و سرور وجود ندارد. حساب های گیت هاب تمام حساب هایی که در سایت گیت هاب وجود دارند دو نوع هستند. حساب کاربری و شخصی حساب سازمانی (تیم) بطور مثال : حساب هایی مانند: Kambiz-Asadzadeh (Kambiz Asadzadeh), BaseMax (Max Base), ccoreghaesm (Ghasem Ramezani), HamedMasafi (Hamed Masafi) همگی حساب های فرد و شخصی هستند. و حساب هایی مانند : Microsoft · GitHub, Google · GitHub, TeamSnap · GitHub, iOSTREAM · GitHub .حساب هایی سازمانی (تیم) هستند هر حساب کاربری و شخصی این قابلیت را دارد تا بتواند یک حساب سازمانی ایجاد کند. ایجاد حساب سازمانی (تیم) برای ایجاد کردن یک حساب سازمانی, بعد از عضویت و ایجاد یک حساب کاربری در Github و وارد شدن به حساب کاربریتان, به اینجا مراجعه کنید تا یک سازمان جدیدی ایجاد کنید. گیت هاب پیج به موضوع اصلی برمیگردیم. تا کنون در مورد انواع حساب ها توضیح داده ایم. حال در نظر داشته باشید که هر حساب کاربری (عادی یا سازمانی) این قابلیت را دارد تا بتواند یک میزبانی برای خودش رزرو کند. در حالت پیشفرض میزبانی ها بصورت رایگان بر روی دامین GitHub Pages قرار دارند، با اینحال بطور مثال اگر شما یک حساب (شخصی یا سازمانی) بنام test456 دارید می‌توانید میزبانی خود را بر روی دامین http://test456.github.io فعال کنید. آزمایشگاه و انجام بصورت عملی در حال حاظر من خودم یک سازمان (تیم) بنام MaxFork با آدرس Max Fork · GitHub در اختیار دارم. می خواهم یک میزبانی وب جدید روی این حساب ایجاد کنم. گزینه New را لمس کنید و یک مخزن جدید (پروژه) ایجاد کنید. دقت کنید که نام پروژه اهمیت دارد را آدرس میزبانی که مد نظرمان هست وارد می کنیم. بطور مثال : MaxFork.Github.io محتوای Description اختیاری است و می توانید هر چه میخواهید وارد کنید. چون بعدا هم امکان ویرایش کردن آن برایتان وجود ندارد. نوع پروژه را می توانید Public یا Private تنظیم کنید. با توجه به اینکه حساب های اکثر افراد معمولی هست گمان میکنم امکان تنظیم کردن از نوع Private برای آنها همراه با میزبانی وب وجود نداشته باشد. بنابراین Public را انتخاب کنید. پیاده کردن یک فایل README برای مخزن اختیاری است و می توانید آنرا ایجاد کنید. همچنین تصمیم شما در مورد انتخاب لایسنس نیز آزادانه است و می توانید خودتان شخصا در مورد آن فکر و تصمیم بگیرید. برای امتحان می توانید فایلی بنام index.html را با یک محتوای آزمایشی ایجاد کنید. در حال حاظر وب سایتی با آدرس https://maxfork.github.io در دسترس وجود دارد و محتوایی را که در فایل نوشته ایم را نشان می دهد. در انتهای بخش Settings در قسمت GitHub Pages می توانید وضعیت میزبانی را بررسی کنید. در قسمت ذکر شده می توانید قالب های از قبل تعریف شده را مشاهده و انتخاب کنید. همچنین امکان تنظیم Custom domain برای میزبانی را هم دارید. همچنین می توانید فایل های دیگری نیز مانند test.html همراه با پوشه و مسیردهی ایجاد کنید. بطور مثال اکنون ما فایل new.html را در شاخه اصلی مخزن ایجاد کردیم که در آدرس زیر قابل دسترس است: https://maxfork.github.io/new.html فایلی بنام _config.yml از قبل تعریف شده است که می توانید بصورت دستی هم ایجاد کنید که در آن نام قالب / تنظیمات / پلاگین و ماژول هایی را که نیاز دارید می توانید تعریف کنید. در قسمت های بعدی در مورد Static بیشتر توضیح خواهم داد، پیشنهاد می‌کنم در مورد Textile و Markdown هم مطالعه کنید. سپاس سید علی محمدیه
  5. 2 امتیاز
    تا کنون در مجموعه‌ی مقالات اصول طراحی، در مورد تعادل و رنگ صحبت کردیم. در این بخش این روند را با موضوع تضاد (Contrast) ادامه می‌دهیم. تضاد هنگامی رخ می‌دهد که دو عنصر در یک صفحه متفاوت باشند. به عنوان مثال، تضاد می‌تواند خودش را در رنگ‌های متنوع بین متن و رنگ پس زمینه نشان دهد. یا یک عنوان بزرگ و درشت در کنار یک فونت sans-serif برای متن بدنه باشد. یا میتواند تفاوت بین یک گرافیک بزرگ و یک گرافیک کوچک باشد و یا ترکیب یک بافت خشن با بافت ظریف که یک تضاد قابل توجهی ایجاد می‌کند. چشمان ما علاقه‌مند به دیدن تضاد هستند. نکته‌ی حائز اهمیتی که در رابطه با تضاد وجود دارد این است که تضاد باید کاملا چشم‌گیر و غیر جزئی باشد. ۱. تضاد از نظر چشمان ما جذاب است. یکی از دلایل استفاده‌ی ما از تضاد، چه در چاپ و چه در وب، جلب کردن توجه مخاطب است. سایت Carsonified از تضاد جهت اثرگذاری استفاده می‌کند. این سایت از متن و عکس‌های درشت و بالعکس و ترکیب رنگ‌های متضاد بهره برده است. همانطور که در زیر می‌بینیم جلد مجله‌ی Proximity، از عکس چندین قایق شناور کوچک در درون دریای آبی تیره استفاده کرده است که تضاد زیبایی را به تصویر کشیده است. ۲. تضاد به سازماندهی اطلاعات کمک می‌کند. استفاده از تضاد نه تنها سبب جذابیت هر چه بیشتر طراحی می‌شود بلکه به هدف و سازماندهی اسناد وضوح بهتری می‌بخشد. در مجله‌ی منتشرشده در زیر، Studio8 از قوانین تضاد، تعادل (Balance) و مجاورت (Proximity) برای ایجاد صفحاتی غیر معمول و چشم‌گیر استفاده کرده است. عناوین درشت مشکی تضاد جالبی با متن ظریف روشن ایجاد کرده‌اند. در ادامه‌ی مجله، Studio8 صفحات را به دو قسمت تقسیم کرده است که از لحاظ ظاهری کاملا متضاد هم هستند. هر صفحه اطلاعاتی را در مورد محصول‌های جداگانه‌ای می‌دهد که به هم مرتبط هستند. مفهوم این ارتباط توسط علامت '&' ادا شده است. ۳. تضاد سبب ایجاد تمرکز می‌شود. آگهی‌ها و تبلیغات مشهور iPod از مفهوم تضاد به صورت کاملا حرفه‌ای برای متمرکز ساختن توجه بینندگان به پخش‌کننده‌ی موسیقی استفاده کرده است. در این تبلیغات یک شخصیت تیره رنگ بر روی زمینه‌ی رنگی قرار گرفته است. iPod و هدفون‌ به رنگ سفید هستند که در مقابل شخصیت تیره رنگ و زمینه‌ی رنگی خود را به خوبی نشان می‌دهند. طراحی برای این بطری سفارشی بر پایه‌ی تضاد بین متن سفید و رنگ قرمز تیره‌ی نوشیدنی است که به عنوان یک کادوی کریسمس، منحصر به فرد است. مواردی که هنگام افزودن تضاد به طرح‌های خود باید به آن‌ها فکر کنید: ۱. چگونه تضاد را ایجاد میکنید؟ از طریق بافت، تایپوگرافی، رنگ یا شکل؟ ۲. اگر می‌خواهید از طریق تایپوگرافی تضاد ایجاد کنید، کدامیک از فونت‌ها را استفاده می‌کنید؟ آیا آن‌ها بسیار متفاوت هستند یا فقط کمی با هم تفاوت دارند؟ گزینههای فونت خود را با دقت بررسی کنید اما به خاطر داشته باشید که متن قابل خواندن باشد. ۳. آیا تضاد به کار رفته، ایده‌ی طراحی شما را تقویت می‌کند؟ پی‌نوشت: مقالات و دوره‌های سایت Sitepoint پیشنهاد می‌شوند. با ایجاد حساب کاربری امکانات جذابی در اختیارتان قرار خواهد گرفت.
  6. 2 امتیاز
    فایل‌ها/تغییرات پروژه را چطوری کنترل کنیم ؟ در وهلهٔ اوّل شاید بگید چه نیازیه ؟ خب برنامه رو می‌نویسیم و میریم دیگه !. درسته برنامه‌اتون را می‌نویسید و می‌روید؛ امّا به کجا چنین شتابان ؟ آیا همیشه برنامهٔ شما کوچک‌خواهد بود ؟ آیا قراره برنامهٔ شما در صد خط تمام بشه ؟ یا اینکه کلاً قصد توسعه‌اش رو دیگه ندارید ؟ خب شاید یکی دیگه داشت :). فرض کنید برنامهٔ‌تان را نوشتید : void parsing(int argc, char **argv){ top = 0; for (unsigned i=1; i < (unsigned)argc; i+=2){ listArgs[top].name = argv[i]; listArgs[top].value = argv[i+1]; top++; } } خب، برنامه‌کار می‌کنه و میرید و یک هفته‌ٔ دیگه میاید و مثلاً خط : top = 0; را حذف می‌کنید. و برنامه در اجرای اوّل درست کار می‌کنه؛ پیش‌خودتون می‌گید خب چه نیازی بود، الکی ماهم کد نوشتیم :). امّا بعداً در اجراهای متوالی برنامه شروع می‌کنه به دادن خروجی‌های نامتعارف. اینجاس که باید ساعت‌ها وقت بزارید و بگردید ببینید آخرین‌بار چه چیزی رو تغییر دادید و کدوم فایل را ویرایش کردید. کار مسخره‌ و اعصاب‌خورد کنی میشه، درسته ؟. امّا برای مدیریت این دَنگٌ‌وفَنگ‌ها می‌تونید از سیستم‌های‌مدیریتپروژه‌ استفاده بکنید. مثل Git حالا اینکه چرا گیت ؟ به خاطر اینکه راحت‌ترینه و بهترینه. چرا ؟ چون امتحانش را پس داده، پروژهٔ بزرگ "کرنل‌لینوکس" را داره مدیریت می‌کنه. حالا بیاید ببینیم اگه ما ازت گیت (git) استفاده می‌کردیم، چطوری می‌توانستیم بفهمیم که چه بلایی سر کد آمده : ۱- اوّل گزارشات را چک می‌کنم، تا ببینم آخرین گزارشی که از تغییرات ذخیره کردم چه بوده ؟: $> git log commit bb513a5f9ec429222de03afa690e7fa5d2fbdf6e (HEAD -> master) Author: Ghasem Ramezani <g1999ramezani@gmail.com> Date: Sun May 5 00:05:22 2019 +0430 create a bug commit ab176fa8a282a74e6badfc285c0986bc66ee6b7d (origin/master, origin/HEAD) Author: Ghasem Ramezani <g1999ramezani@gmail.com> Date: Sat May 4 10:40:32 2019 +0430 make `top` to be 0 at first of parsing() function and make class storage of listArgs to be `extern` and getOption() function return "NULL" on Failure. خب فهمیدم که آخرین تغییرم با عنوان create a bug ثبت شده، حالا باید از شناسه‌اش استفاده کنم. ۲- تغییراتی که در آن گزارش ثبت شده است را مشاهده می‌کنم. : $> git show bb513a5f9ec429222de03afa690e7fa5d2fbdf6e commit bb513a5f9ec429222de03afa690e7fa5d2fbdf6e (HEAD -> master) Author: Ghasem Ramezani <g1999ramezani@gmail.com> Date: Sun May 5 00:05:22 2019 +0430 create a bug diff --git a/source/arg.c b/source/arg.c index c776ff2..a75c91d 100644 --- a/source/arg.c +++ b/source/arg.c @@ -7,7 +7,6 @@ unsigned top=0; struct ARGS listArgs[MAX_ARG]; void parsing(int argc, char **argv){ - top = 0; for (unsigned i=1; i < (unsigned)argc; i+=2){ listArgs[top].name = argv[i]; listArgs[top].value = argv[i+1]; دیدی به چه سادگی توانستیم تغییری که دادیم را پیدا کنیم ؟ البته این انتهای ماجرا نیست ! الآن که متوجه شدیم در کدام گزارش‌ما خراب‌کاری کردیم؛ کافیه که تغییرات را به گزارش قبل از خراب‌کاری برگردانیم : $> git reset --hard ab176fa8a282a74e6badfc285c0986bc66ee6b7d البته قابل ذکره که ما اینجا تنها داخل این گزارش فقط یک تغییر داشتیم، مسلماً کار می‌تونه کمی پیچیده‌تر بشه اگه تغییرات زیاد باشن، که همیشه هستن . چگونه با گیت (git) کار کنیم ؟ بسیار ساده، مسلماً اوّل نیاز دارید که این برنامه را نصب کنید. این برنامه به طور پیش‌فرض در سیستم‌عاملتون نصب نیست. کافیه که از مدیربستهٔ سیستم‌عاملتون کمک بگیرید، مثلاً برای Debian - Ubuntu - Ubuntu Mint به این‌صورت کار تمام می‌شود : $ apt install git حالا بعد از نصب، نیاز دارید که مشخصاتتان را ثبت کنید، دقت کنید که تمام توضیحاتی که بنده می‌دهم را می‌توانید به‌صورت کامل‌تر از سایت گیت (git) دنبال کنید. $> git config --global user.name "Ghasem Ramezani" $> git config --global user.email "g1999ramezani@gmail.com" $> git config --global core.editor emacs دو مورد اوّل که واضح هستن، امّا مورد آخر دل‌بخواه خودتان هست، زمانی‌که نیاز باشه گیت (git) ویرایشگرمتنی را جهت ویرایش‌باز بکند باید بداند که کدام ویرایشگر مورد علاقهٔ شماست. می‌توانید هر برنامه‌ای را قرار بدهید. امّا دقت کنید که بهترین ویرایشگر‌ها می‌توانند Vim, Emacs, Notepad++ باشند؛ فایل این تنظیمات را می‌توانید از این مسیرها دنبال کنید : User Space : ~/.gitconfig System Wide: /etc/gitconfig ساخت مخازن (repository) خب حالا که نصب/پیکربندی انجام شد، کافیه که مخزن (repository) خودمان را راه‌اندازی کنیم. یک پروژهٔ جدید درست کنید و گیت (git) را مقداردهی (Initialize) کنید : $> mkdir project ; cd project $> git init ما یک دایرکتوری به اسم project درست کردیم، و مخزن (repository) خودمان را با دستور git init راه‌اندازی کردیم، یک سری فایل‌هایی گیت (git) برای ما داخل آن دایرکتوری با اسم .git درست کرده. تغییراتی‌که نیاز رو انجام میدیم، مثلاً در وهلهٔ اوّل دایرکتوری‌ها و فایل‌های پروژه را راه‌اندازی می‌کنیم : $> mkdir header source build object $> touch header/arg.h source/arg.c Makefile $> tree . ├── build ├── Makefile ├── header │ └── arg.c ├── object └── source └── arg.h 4 directories, 3 files $> اگه درمورد Makefile نمی‌دانید، می‌توانید از اینجا با GNU Make و Makefile آشنا بشید. الآن بد نیست که خروجی دستور git status را ببینیم تا توضیحاتی در این‌باره بدیم (این دستور، وضعیت‌جاری مخزنمان را نشان می‌دهد) : $> git status On branch master No commits yet Untracked files: (use "git add <file>..." to include in what will be committed) Makefile header/ source/ nothing added to commit but untracked files present (use "git add" to track) عکس زیر را مشاهده‌کنید تا توضیح‌بهتری بدم : فایل‌های شما داخل گیت (git) دارای حالات‌های مختلفی‌هستن، به طورکلّی یا شناخته‌شدن‌اند (tracked) یا ناشناخته‌اند (untracked)؛ فایل‌ها/دایرکتوری‌هایی که ما بعد از مقدار‌‌دهی مخزن‌مان ساختیم، در حالت ناشناخته (untracked) هستند. که خود گیت (git) هم همین‌را به ما گفته‌است : Untracked files: (use "git add <file>..." to include in what will be committed) برای اینکه شناخته‌شده (tracked) بشند، باید آنها را به صحنه (stage) ببریم. برای اینکار خود گیت گفته‌است که باید چه کرد که می‌توانیم به دوصورت انجام دهیم : $> git add Makefile source header $> git add -A خب حالا دوباره خروجی git status را نگاه می‌کنیم : $> git status On branch master No commits yet Changes to be committed: (use "git rm --cached <file>..." to unstage) new file: Makefile new file: header/arg.c new file: source/arg.h الآن فایل‌های ما به stage رفتند، و آمادهٔ این‌هستند که گزارش‌ (commit) بشوند. دقت کنید که Git از خِیر دایرکتوری‌های خالی می‌گذرد. حالا کافیه‌که ما تغییراتی که دادیم را گزارش کنیم، که با دستور git commit به دوصورت انجام می‌شود : $> git commit $> git commit -m "My Message" در حالت‌اوّل، گیت ادیتور پیش‌فرضتان را باز می‌کند و از شما می‌خواهد که یک توضیح‌کوتاه درمورد تغییراتی‌که داده‌اید بنویسید، در حالت‌دوّم، شما مستقیم توضیح‌کوتاه خود را وارد می‌کنید. حال دوباره برگردیم و خروجی دستور git status را ببینیم : $> git status On branch master nothing to commit, working tree clean خیلی‌هم عالی، این نشان دهندهٔ این‌است که ماهیچ فایل ناشناخته (َUntracked) یا دستکاری‌شده (Modified) یا درصحنه (Stage) نداریم. هنگامی‌که تغییراتی را در فایل‌های شناخته‌شده (Tracked) بدید، آن فایل از حالت دستکاری‌نشده (Unmodified) به حالت دستکاری‌شده (Modified) درمیاید، که نیاز است شما تغییرات را درصورت‌نیاز وارد صحنه (Stage) کنید و بعد گزارش‌کنید (Commit). حال تغییراتی‌اعمال می‌کنیم، و مراحل‌مورد نیاز تا درصحنه (Stage) بردن‌فایل‌ها انجام می‌دهیم : $> git add -A $> git status On branch master Changes to be committed: (use "git reset HEAD <file>..." to unstage) modified: Makefile modified: header/arg.h modified: source/arg.c امّا شاید شما نخواید که مثلاً Makefile گزارش‌ش با مابقیه فایل‌ها یکی باشه، و نیاز دارید که از Stage بیرون بیاریدش؛ دقّت کنید خود Git هم راهنمایی‌ کرده که باید چه‌کار کرد : $> git reset HEAD Makefile $> git status On branch master Changes to be committed: (use "git reset HEAD <file>..." to unstage) modified: header/arg.h modified: source/arg.c Changes not staged for commit: (use "git add <file>..." to update what will be committed) (use "git checkout -- <file>..." to discard changes in working directory) modified: Makefile و حالا می‌توانید به راحتی گزارش فایل‌های خودتان را برای header/arg.h و source/arg.c بنویسید : $> git commit -m "Done With Print() Function" فایل .gitignore بیاید تا make را اجرا کنیم (درصورتی‌که با GNU Make آشنا نیستید، برای آشنایی این‌قسمت را مطالعه کنید) : $> git status On branch master Changes not staged for commit: (use "git add <file>..." to update what will be committed) (use "git checkout -- <file>..." to discard changes in working directory) modified: Makefile Untracked files: (use "git add <file>..." to include in what will be committed) build/ object/ no changes added to commit (use "git add" and/or "git commit -a") اینجا دایرکتوری‌های build و object هم اضافه شدند،امّا ما نیازی نداریم که Git این دایرکتوری‌ها را مدیریت کند، پس کافیه که یک فایل به اسم .gitignore‌ درست کنیم. و فایل‌ها و دایرکتوری‌هایی که نمی‌خواهیم Git آنها را دنبال کند را در آن ذکر کنیم : $> echo -e "/build/*\n/object/*" > .gitignore $> cat .gitignore /build/* /object/ $> git status On branch master Changes not staged for commit: (use "git add <file>..." to update what will be committed) (use "git checkout -- <file>..." to discard changes in working directory) modified: Makefile Untracked files: (use "git add <file>..." to include in what will be committed) .gitignore no changes added to commit (use "git add" and/or "git commit -a") و مشاهده می‌کنید که دیگر Git اخطاری برای دایرکتوری‌های build‌ و object نداد. خب دوستان،امیدوارم دلیل اهمیّت Git و کلاً برنامه‌های کنترل‌ورژن را درک‌کرده باشید؛ امّا شرمنده، دنیای Git بزرگ‌تر از آنچه هست که من بخوام خلاصه‌ای از هر قسمت را در یک پست باز‌گو کنم؛ شدیداً پیشنهاد می‌کنم که این‌کتاب را برای فراگیری هرچه بهتر Git‌ بخوانید. - موفق و پیروز باشید.
  7. 2 امتیاز
    زبان نشانه‌گذاری Markdown یا به اختصار MD همانند زبان HTML برای طراحی قالب متن استفاده می‌شود. به گمانم که قبلاً نمونه‌های زیادی را تحت عنوان README.md شنیده‌باشید. مانند نمونهٔ زیر : استفاده از این قالب بسیار ساده‌ است، حال بخشی از نحوهٔ نگارش فایل‌های MD را می‌گوییم. برای نوشتن سر‌نویس‌ها (Header) : Header1 ======= # Header1 ## Header2 ### Header3 برای "خط‌جدید" (NewLine) از دو کاراکتر Space در انتهای هر پاراگراف استفاده می‌کنیم و یا از دو/n (توسط کلید ReturenKey) استفاده می‌کنیم : first line. second line. ویژگی‌های متن : _italic_ or *italic* __bold__ or **bold** `monospace` نوشن یک بلاک کد : ```<Language-Name> // And Write Code Here ``` ```c int main (int argc, char **argv, char **envp){ /* some code here */ return EXIT_SUCCESS; } ``` or indent your code by four space: int main (int argc, char **argv, char **envp){ /* some code here */ return EXIT_SUCCESS; } انواع لیست‌ها : Bullet List: * One Option * Another Option Numbered List: 1. Free Software 2. GNU/Linux Todo List: - [x] it's done. - [ ] working on it. نوشتن پیوند‌ها : [GNU/Linux](www.kernel.org) ![Image](https://en.wikipedia.org/wiki/File:Qt_logo_2016.svg) استفاده از حالت "نقل‌قول" : > for using blockquoting in md files. استفاده از جداول : Prorgamming Language | It's God ? ---------------------|------------ C | Very GOD C++ | It's God Python | :( خروجی تمام نمونه‌های بالا : استفاده از قالب‌های گفته شده، بستگی به پیاده‌سازی موتور رندر ویرایشگری دارد که شما از آن استفاده می‌کنید، چرا که ممکن است تمام قابلیت‌ها را پیاده‌سازی نکرده‌باشد. موفق‌وپیروز باشید.
  8. 2 امتیاز
    بارها بوده که برنامه‌‌ای را نوشته‌ایم، روند کامپایل و اجرا به خوبی و خوشی انجام می‌شود. امّا در مرحلهٔ اجرای برنامه، خروجی‌های نامناسبی پدیدار می‌شود. که متأسفانه چیزی نیستند که ما می‌خواهیم. خب برای حل این مشکل دو راه پیش‌رو می‌باشد : بازبینی کد و انجام تست برای یافتن محل مشکل. استفاده از ابزارهای خطایابی (Debugging). بازبینی کد و انجام تست برای یافتن محل مشکل برای این‌کار فریورک‌ها و کتابخانه‌های زیادی موجود می‌باشد، مثلاً کتابخانهٔ تست‌نویسی (Test Case) به اسم Catch2. کار با این کتاخانه بسیار آسان است. برای مثال تابع زیر را در نظر داشته‌باشید : unsigned int Factorial( unsigned int number ) { return number <= 1 ? number : Factorial(number-1)*number; } ما می‌خواهیم بدانیم که آیا این تابع خروجی مناسب را دارد یا خیر، درصورتی‌که از catch2 استفاده می‌کنید، کافیست که طبق راهنمای README.md هدرفایل catch.cpp را دریافت و به برنامهٔ خود اضافه کنید : #define CATCH_CONFIG_MAIN // This tells Catch to provide a main() - only do this in one cpp file #include "catch.hpp" unsigned int Factorial( unsigned int number ) { return number <= 1 ? number : Factorial(number-1)*number; } TEST_CASE( "Factorials are computed", "[factorial]" ) { REQUIRE( Factorial(1) == 1 ); REQUIRE( Factorial(2) == 2 ); REQUIRE( Factorial(3) == 6 ); REQUIRE( Factorial(10) == 3628800 ); } و بهصورت نمونهٔ کد بالا از catch2 استفاده می‌کنیم. طبق اسناد با تعریف ماکروی CATCH_CONFIG_MAIN ما یک تابع main توسط خود catch2 تعریف می‌کنیم. و کافیه که فقط این سورس را کامپایل و اجرا کنیم : $> g++ -o catch2Test main.cpp $> $> ./catch2Test =============================================================================== All tests passed (4 assertions in 1 test case) و خب مسلماً درصورتی‌که خطایی باشد در این آزمایشات معلوم می‌گردد،‌ فریمورک‌های دیگری مانند Google Test نیز موجود می‌باشد. استفاده از ابزارهای خطایابی (Debugging) در این روش شما برنامهٔ خود را تحت برنامهٔ دیگری اجرا و اقدام به خطایابی می‌کنید، یکی از برنامه‌های خطایابی معروف و آزاد، برنامهٔ GNU Debugger می‌باشد. از این برنامه برای خطایابی در زبان‌های : Ada Assembly C ++C D Fortran Go Objective-C OpenCL Modula-2 Pascal Rust استفاده می‌شود. برای نصب می‌توانید از مدیربستهٔ سیستم‌عامل خود استفاده کنید : $> apt install gdb gdb-doc و یا اینکه از این پیوند برنامه را دریافت و اقدام به کامپایل/نصب آن کنید. نکته : دقت کنید که همیشه نباید حتماً خطایی در برنامه باشد تا اقدام به خطایابی کنیم، گاهی هم نیاز است که روند کار برنامه را به این روش با استفاده از ابزارهای مشابه پی‌گیری کنیم. (In fact: reverse engineering) حال اقدام به Debug کردن این برنامهٔ کوتاه می‌نماییم : #include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <unistd.h> static int data = 0x02A; int main (void){ int stack = 0x2FF; pid_t pid; switch (pid = fork()){ case -1: perror("pid = fork()"); exit(EXIT_FAILURE); case 0: data |= 0x02B; stack &= data; break; } fprintf(stdout, "[%s]\v [PID=%ld] [data=%d] [stack=%d].\n", (pid == 0) ? "child" : "parent", (long) getpid(), data, stack); exit(EXIT_SUCCESS); } قبل‌از اینکه برنامه‌ای را برای دیباگ‌ کردن داخل GDB استفاده کنیم،‌ نیاز است که برای کمک به این روند اطلاعاتی مانند مکان Source Code برنامه را به فایل باینری خود اضافه کنیم. برای اینکار کافیست که از فلگ -g یا -ggdb یا -g3 استفاده کنیم. این پیوند را برای اطلاعات بیشتر مطالعه کنید. به این‌صورت برنامه را کامپایل و برای دیباگ کردن آماده می‌کنیم : $> gcc -o output -ggdb main.c توجه کنید که سورس برنامه را از مکانش تغییر ندهید. حال برنامهٔ gdb را با نوشتن اسم gdb در shell فراخوانی می‌کنیم : $> gdb GNU gdb (Debian 8.2.1-2) 8.2.1 Copyright (C) 2018 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html> This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. Type "show copying" and "show warranty" for details. This GDB was configured as "x86_64-linux-gnu". Type "show configuration" for configuration details. For bug reporting instructions, please see: <http://www.gnu.org/software/gdb/bugs/>. Find the GDB manual and other documentation resources online at: <http://www.gnu.org/software/gdb/documentation/>. For help, type "help". Type "apropos word" to search for commands related to "word". (gdb) برای اینکه فایل باینری را باز کنیم باید از دستور file استفاده کنیم. همچنین می‌توانیم به این‌صورت نیز برنامهٔ gdb را به همراه فایل باینری خود اجرا نماییم : $> gdb output خب،‌ در این قسمت می‌توانیم با استفاده از دستور run برنامهٔ خودمان را اجرا کنیم و درصورتی‌که نیاز باشد آرگومان‌هایی را نیز ارسال کنیم. وقتی دستور run‌ را وارد می‌کنیم برنامهٔ ما اجرا می‌شود و خاتمه میابد. زمانی‌که نیاز داریم روند اجرای برنامه در نقطه‌های مشخصی متوقف شود باید از ‌Break Point استفاده کنیم. برای قرار دادن Break Point در خط‌های مختلف برنامه از دستور break به اضافهٔ شمارهٔ خط سورس برنامه استفاده می‌کنیم. برای اینکار بد نیست که اطلاعاتی نیز درمورد سورس‌کد خود داشته‌باشیم مثلاً همزمان بتوانیم سورس را نیز مشاهده کنیم، درصورتی‌که از ادیتور Emacs استفاده می‌کنید می‌توانید M+x را زده و gdb را اجرا کنید. که یک بافر در سمت چپ برای شما باز می‌کند (درصورتی‌که دکمه‌های Ctrl + X و Ctrl + 3 را زده باشید). در غیر این‌صورت به دو روش دیگر می‌توانید سورس خود را هنگام دیباگ مشاهده کنید، در روش اوّل استفاده از دستور list به اضافهٔ دو آرگومان می‌باشد : آرگومان اوّل مشخص کنندهٔ خط شروع است، و آرگومان دوّم مشخص کننده خط پایان هست. مثلاً با فراخوانی list 2, 10 از خط دو تا ده را نمایش می‌دهد : (gdb) list 2, 10 2 #include <stdlib.h> 3 #include <sys/types.h> 4 #include <unistd.h> 5 6 static int data = 0x02A; 7 8 int 9 main (void){ 10 int stack = 0x2FF; می‌توانید فقط یک آرگومان ارسال کنید، که به اندازه ی مقدار متغیر listsize که پیشفرض ده می‌باشد (می‌توانید با استفاده از دستور set مقدار را تغییر دهید) را نمایش می‌دهد : (gdb) list 2 1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <sys/types.h> 4 #include <unistd.h> 5 6 static int data = 0x02A; 7 8 int 9 main (void){ 10 int stack = 0x2FF; حالات دیگه‌ای هم دارد که می‌توانید با وارد کردن help list متوجه شوید. امّا روش بهتری (روش دوّم) نیز برای دیدن سورس برنامه به همراه دیباگ کردن وجود دارد،‌ می‌توانید از رابط TUI استفاده کنید. برای استفاده از این رابط دکمه‌های Ctrl + X و Ctrl + A یا < وارد کرده و ReturnKey (یا همان Enter) را بزنید. در این قسمت شما به راحتی می‌توانید هم سورس برنامهٔ خودتان را ببینید و هم برنامه را دیباگ کنید. با یک‌بار اجرا کردن برنامه توسط دستور run سورس برنامه‌ٔ شما بارگذاری می‌شود. برویم به سراغ Break Point گذاشتن، برای مثال ما می‌خواهیم یک Break Point بر سر خط ۱۰ و ۱۳ بگذاریم : (gdb) break 11 Breakpoint 1 at 0x555555555185: file main.c, line 13. (gdb) break 10 Breakpoint 2 at 0x55555555517e: file main.c, line 10. (gdb) دوباره برنامه را با وارد کردن دستور run اجرا می‌کنیم، تا اجرای برنامه این‌بار در برخورد با اوّلین Break Point متوقف شود : در این توقف، ما می‌توانیم با استفاده از دستور print محتویات متغیرهارا مشاهده کنیم : (gdb) print data $1 = 42 (gdb) print stack $2 = 21845 دستور print قابلیت‌های جالبی دارد : (gdb) print 12 / 2 $1 = 6 (gdb) print sizeof(int) $2 = 4 (gdb) print &data $3 = (int *) 0x4a60f0 <data> (gdb) مقادیری که به ترتیب در سمت چپ شماره‌گذاری شده‌اند درواقع اسم متغیرهایی هستنند که خروجی در آن قرار گرفته است : (gdb) print $1 $4 = 42 (gdb) print $4 $5 = 42 (gdb) همچنین با استفاده از دستور delete می‌توانیم یک Break Point را حذف کنیم. برای ادامه دادن به روند اجرای برنامه تا Break Point بعدی از دستور continue و برای رفتن به خط بعدی از دستور next استفاده می‌کنیم.بعد از اجرای دستور next دقت کنید سریعاً به خط 23 رفته و فراخوانی تابع سیستمی fork() را رها می‌کند. به خاطر اینکه دستور next کاری به توابعی که فراخوانی کرده‌اید، در اینجا فراخوان سیستمی fork() ، ندارد و دستورات سورس شما را ادامه می‌دهد؛ امّا درصورتی‌که از step یا stepi استفاده بکنید وارد دستورات شده و دستورات توابع شما را پیمایش می‌کند: (gdb) next [child] [PID=563] [data=43] [stack=43]. [Detaching after fork from child process 563] (gdb) نکته : رابط TUI زیاد قوی نمی‌باشد،‌ لذا درصورتی‌که خروجی چاپ شود تنظیمات صفحه نمایش را به هم می‌زد می‌توانید با زدن Ctrl + L خروجی‌های اضافه را از بین ببرید. برای دیباگ کردن یک fork() می‌توانید مقدار follow-fork-mode را ویرایش کنید : (gdb) set follow-fork-mode child (gdb) run Starting program: /tmp/output Breakpoint 2, main () at main.c:10 (gdb) next Breakpoint 1, main () at main.c:13 (gdb) step [Attaching after process 2387 fork to child process 2403] [New inferior 2 (process 2403)] [Detaching after fork from parent process 2387] [Inferior 1 (process 2387) detached] [Switching to process 2403] main () at main.c:13 (gdb) همچنین با استفاده از دستور disassemble می‌توانید سورس اسمبلی یکی تابع را مشاهده کنید : (gdb) disassmble main Dump of assembler code for function main: 0x0000000000401b4d <+0>: push %rbp 0x0000000000401b4e <+1>: mov %rsp,%rbp 0x0000000000401b51 <+4>: push %rbx 0x0000000000401b52 <+5>: sub $0x18,%rsp => 0x0000000000401b56 <+9>: movl $0x2ff,-0x14(%rbp) 0x0000000000401b5d <+16>: callq 0x43c9b0 <fork> 0x0000000000401b62 <+21>: mov %eax,-0x18(%rbp) 0x0000000000401b65 <+24>: cmpl $0xffffffff,-0x18(%rbp) 0x0000000000401b69 <+28>: je 0x401b73 <main+38> 0x0000000000401b6b <+30>: cmpl $0x0,-0x18(%rbp) 0x0000000000401b6f <+34>: je 0x401b89 <main+60> 0x0000000000401b71 <+36>: jmp 0x401ba2 <main+85> 0x0000000000401b73 <+38>: lea 0x7c48e(%rip),%rdi # 0x47e008 0x0000000000401b7a <+45>: callq 0x408bd0 <perror> 0x0000000000401b7f <+50>: mov $0x1,%edi 0x0000000000401b84 <+55>: callq 0x408010 <exit> 0x0000000000401b89 <+60>: mov 0xa4561(%rip),%eax # 0x4a60f0 <da --Type <RET> for more, q to quit, c to continue without paging-- لینک منبع را برای ادامهٔ داستان دنبال کنید :). موفق و پیروز باشید.
  9. 2 امتیاز
    سلام، کتابخانه‌های Win32 و MFC هرچند کتابخانه‌های قدرتمندی هستند اما باید در نظر داشته باشید این‌ها مُختصِ پلفترم ویندوز بوده و بر اساس API های ویندوز ارائه شدن و مسلماً کاربرد‌های آن‌چنان به‌روز و چند منظوره‌ای مثل Qt رو ندارند. کتابخانه‌ی پیشنهادی خود مایکروسافت در قالب چهارچوب دات‌نت است که برای سی++ هم UWP قابل استفاده بوده و شما می‌تونید رابط کاربری مدرن رو هم با WPF توسعه دهید (اما انتظار نداشته باشید در حد Qt خارق‌العاده باشه). با توجه به اینکه شخصاً تعامل خوبی با دات‌نت و سی‌شارپ دارم و در مواقعی که صلاح می‌دونم ازش استفاده می‌کنم، نیاز هست تا شمارو در جریان یک واقعیت قرار بدم (که سوال بسیاری از دوستان بوده)! شما برای اینکه برنامه‌ای رو تحت هر زبانی اجرا کنید مسلماً نیاز به یک سری کتابخانه‌ها و پیش نیازاتی خواهید داشت! برای مثال در سی‌شارپ شما نیاز به دات‌نت دارید و در سی++ نیاز به STL، Qt، ‌Boost و غیره! حالا کتابخانه‌ی پیشفرض سی++ STL و پیشفرض سی‌شارپ Net. هست! حالا با توجه به اینکه شما یک برنامه‌ی ساده ز نوع "سلام دنیا!" بنویسید! چیزی که در سی++ ارائه خواهد شد حدود چند کیلوبایت است که برای اجرای اون تنها نیاز به فایل‌های msvcr و msvcp و یا vcruntime خواهید بود که جمعاً حدود ۱ تا ۲ مگابایت نیستند! اما برعکس در سی‌شارپ شما بخوای همین برنامه‌ی ساده رو بنویسی خبری از این سادگی نیست! نیازمند پکیج حجیمی از دات‌نت خواهی بود که باید نصبش کنی در ویندوز وقتی شما با سی‌شارپ برنامه‌نویسی می‌کنید به دلیل اینکه چهارچوب .Net و SDK‌های مربوط به دات‌نت بر روی سیستم‌عامل مستقر شده‌اند نیازی نیست تا کتابخانه‌های مربوط به دات‌نت رو در کنار برنامه‌ی خودتون قرار بدین (چون این‌ها در خود سیستم‌عامل نصب می‌شوند نه در کنار برنامه). دقت کنید آیا بدون نصب پکیج (Microsoft .NET Framework Redistributable) می‌تونید برنامه‌های مربوطه رو اجرا کنید؟ پاسخش مسلماً خیر خواهد بود! این پکیج حداقل بعد از نصب چیزی حدود ۲۰۰ تا ۷۰۰ مگابایت و حتی بیشتر کتابخانه‌ی دات‌نت استخراج خواهد کرد و این یعنی در کنار فایل اجرایی یک برنامه‌ی ساده از نوع "سلام‌ دنیا!" چنین حجم بزرگی از کتابخانه نیاز خواهد بود! اما شما متوجه این نمی‌شید چون به صورت پیشفرض موقع نصب برخی از ابزار‌های اختصاصی مایکروسافت مثل ویژوال استودیو، آفیس و غیره این پکیج نصب می‌شود! البته خارج از لطف هم نیست در برخی از نسخه‌های نهایی ویندوز مثل ویندوز ۱۰ بخش هسته‌ی کتابخانه همراه با هسته‌ی سیستم‌عامل ارائه میشه و آنچنان مثل قبل نیاز نصب بخش عظیم کتابخانه نیست. اما با این حال شما حتماً به پکیج مربوطه جهت اجرای تمامی قابلیت‌های برنامه‌ی خودتون نیاز خواهید داشت که اصلاً قابل مقایسه با یک برنامه‌ی ۱ تا ۲ مگابایتی تحت سی++ نیست. حالا با توجه به این آیا حجمی معادل ۸ تا ۲۰ مگابایت واقعاً مشکل محسوب می‌شود؟! شما فرض کن کتابخانه‌ی کیوت رو در قالب یک SDK روی هسته‌ی سیستم‌عامل خودت نصب کرده باشی! در این صورت همون فایل اجرایی چند کیلوبایتی نهایت حجم تولید شده از یک برنامه‌ی ساده است. تعداد فایل‌ها مربوط به همون کتابخانه‌ هستش! آیا می‌دونید تعداد فایل‌های دات‌نت بسیار بیشتر و در یک کلام چند برابر کتابخانه‌ی کیوت است!؟ برای اینکه متوجه واقعیتی (پنهان) شوید به این مسیر بروید: C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework وحشتناکه نه؟! مایکروسافت به همین راحتی سر برنامه‌نویس‌ها شیره می‌ماله و اون‌ها رو توجیح کرده که اگر کمی زیرک و حرفه‌ای به این مسائل باشید خواهید فهمید که در پشت این مزیت خوب چه جهنمی ساخته شده! در شناخه‌تر بودن محیط VS شکی نیست، اما این محیط توسعه یک محیط انحصاری برای پلتفرم ویندوز است. محیط توسعه‌ی Qt در هماهنگی بالا با خود کتابخانه مخصوصاً پشتیبانی از سیستم‌عامل‌های دیگه بسیار بهتر عمل می‌کنه. در مورد شباهتش به Win32 هم به خاطر درگیری مستقیم شما با کتابخانه هست. در رابطه با اینکه رابط کاربری رو با سی‌شارپ بنویسید و بک‌اند رو با سی++ کاملاً مخالفم! چون دارین خودتون رو قول می‌زنید! اگه قرار هست این کارو بکنید خب با همون VS تحت دات‌نت و C++/CLR کد‌نویسی کنید که تحت دات‌نت خواهد بود. راه حل بهتر اینه که واقعیت رو بپذیرید و قبول کنید که هر کار خارق‌العاده‌ای نیاز به تعامل بیشتری خواهد داشت. اگر می‌خواهید در قالب سرعت، قدرت، دسترسی‌، تعامل و خارج از هرگونه محدودیت برنامه‌نویسی کنید سی++ رو باید با تمامی سختی‌هاش بپذیرید! در غیر این صورت هیچ روشی برای قانع کردن خودتون وجود نداره. درضمن پاسخ اصلی به عنوان سوال : به صورت پیشنهادی کتابخانه‌ی Qt هست. کتابخانه‌های دیگری هم هستند مثل Juce یا wxWidgets.
  10. 1 امتیاز
    استفاده از قابلیت جدید تعریف شده در دستور using جهت ایجاد نوع داده ای جدید در C++11 به بعد... در استاندارد های قدیمی زبان برای تعریف نوع های داده ای جدید از دستور typedef استفاده میکردیم، به طور مثال فرض کنید که آرایه هایی را به این شکل تعریف میکردیم... typedef char byte; typedef int dword; typedef byte TmpDataBuf[20]; typedef byte ByteBuf[10]; typedef dword wordsBuf[15]; قطعا تصدیق میکنید که نحوه تعریف بدین شکل کمی مشکل و نگهداری برنامه را قطعا دچار مشکل خواهد کرد، لذا در استاندارد جدید تعریف شده در زبان می توانید با استفاده از دستور using را به صورت یک template تعریف نمایید.بدین ترتیب استفاده از template ها در دستورات تعریفی همچون using قابل استفاده شده و تعریف نوع واقعی به زمان اجرا انتقال داده شده است. using byte = char ; using dword = int ; template<typename T,size_t N = 15> using newDataType= T[N]; //usage newDataType<byte,20> TmpDataBuf; newDataType<byte,10> ByteBuf; newDataType<dword> wordsBuf; و قطعا تصدیق میکنید که استفاده از این نوع تعریف بسیار کاربردی تر و نگهداری کد را به مراتب بالاتری در پی خواهد داشت.
  11. 1 امتیاز
    درسته که تعداد فایلهای کتابخانه دات نت خیلی زیاد هست و وابستگی ایجاد میکنه ولی توسعه یک نرم افزار تجاری (البته کشور خودمون را عرض میکنم) توسط یک شرکت نرم افزاری معتبر قطعا با دات نت ویا تکنولوژی های جاوا صورت میگیره! و در صورتی هم که حوزه نرم افزار مربوط به مسائل کلان کشور باشه قطعا لینوکس و زبان ++C,C اولین گزینه انتخاب برای توسعه خواهد بود. به همین علت به نظرم بهتره که هیجان زده نشید و نسبت به مایکروسافت یه جوری نشید
  12. 1 امتیاز
    سلام؛ خوش‌آمدید، لطفاً ده-دقیقه وقت بگذارید و اسناد زیر را جهت فعالیت در انجمن مطالعه بکنید : قوانین نگارشی جهت نشر محتوا سند نحوه‌ی پرسش و پاسخ هوشمندانه سیستم‌عامل گنو/لینوکس، یک سیستم‌عامل آزاد - متن‌باز می‌باشد، که می‌توانید به رایگان آن را دریافت و استفاده کنید : سیستم‌عامل گنو/لینوکس و جنبش نرم‌افزار آزاد آزاد به چه معنی است ؟ نسخه‌های متعدد و زیادی از سیستم‌عامل‌های گنو بر پایه کرنل لینوکس موجود می‌باشد که می‌توانید هرکدام را به رایگان دریافت و استفاده بکنید : دریافت سیستم‌عامل آزاد دبین. جهت شروع آشنایی کار با این سیستم‌عامل‌ها می‌توانید از این دورهٔ آموزشی آقای‌میر‌میرانی شروع کنید : دورهٔ آموزشی مدرک LPIC-1 نیازی به بارگیری SDK و NDK دوباره نیست، ولی برای JDK بسیار بهتر می‌باشد که از طریق مدیربستهٔ توزیعتان اقدام به نصب آن کنید که اختلالی پیش نیاید. مطمئن شوید که این سند را نیز مطالعه کردید : کیوت برای اندروید.
  13. 1 امتیاز
    آقای اسدزاده ممنونم ❤ ولی حِسم به مایکروسافت یه جوری شد ☺
  14. 1 امتیاز
    اینو ببین. مثال همین چیزی رو که میخوای رو نوشتم QML-Samples/AppNavigation at master · HamedMasafi/QML-Samples · GitHub
  15. 1 امتیاز

    نگارش 5.0

    هدف از این آموزش‌ها آشنایی با امکاناتی که در Qt می‌توان استفاده کرد می‌باشد، که شامل کدنویسی کمتر، خروجی و طرح های بیشتر و در نهایت استفاده در پلتفرم‌های مورد مختلف است. لذا جهت بهره‌مندی از این کتابخانه ما با در نظر گرفتن اینکه علاقه‌مندان با زبان ++C آشنایی لازم را دارند منتشر کرده‌ایم. بنابراین در صورتی که علاقه‌مندان اطلاعات کافی در رابطه با خود زبان ندارند پیشنهاد می‌کنیم ابتدا اقدام به تهیه و مطالعه آموزش‌های لازم در ++C نمایند که برخی از لینک‌های رسمی و استاندارد آن را در زیر اعلام نموده‌ایم. عنوان این آموزش "برنامه نویسی ++C همراه با کتابخانه های Qt 5.12.x (سطح مقدماتی ویرایش ۵) می‌باشد که به صورت زیر فهرست بندی شده است: فصل اول مقدمه کتابخانه Qt قابلیت ها در طراحی فناوری Qt Quick و QML نسخه های کیوت مجوز های موجود در این کتابخانه محیط های توسعه کیوت ویژگی های کیوت پشتیبانی از انواع سیستم عامل ها نصب و پیکربندی Qt فصل دوم انواه پروژه و ایجاد آن انواع پروژه ها ایجاد پروژه فصل سوم ساده ترین برنامه معرفی و کار با Signal و Slot ها و Event ها معرفی و کار با نمایش Windows معرفی و کار با لایه ها زبانه ها و بدنه های در طراحی معرفی و کار با قابلیت های HTML و CSS در طراحی فصل چهارم معرفی و کار با لایه های افقی و عمودی معرفی و کار با لایه های Grid در طراحی فرم معرفی و کار با جدا کننده ها Splitter فصل پنجم معرفی و کار با دایرکتوری ها معرفی و کار با فایل ها / خواندن و نوشتن در آن ها فصل ششم معرفی و کار با برچسب ها Label معرفی و کار با دکمه ها Button معرفی و کار با کنترل ورودی LineEdit معرفی و کار با چک باکس CheckBox معرفی و کار با RadioButton معرفی و کار با Combobox معرفی و کار با لیست ها / ListWidget معرفی و کار با لیست های درختی / TreeWidget معرفی و کار با Action ها معرفی و کار با Slider و Progress ها معرفی و کار با Statusbar در فرم فصل هفتم معرفی و کار با MessageBox معرفی و کار با Timer معرفی و کار با Thread ها فصل هشتم معرفی و کار با Map معرفی و کار با Hash معرفی و کار با QStringList لیست رشته ای فصل نهم معرفی و کار با الگوریتم های معرفی و کار شبکه / دانلود فایل بر اساس پروتکل های HTTP و FTP معرفی و کار با باینری و سریالیز کردن آبجکت ها معرفی و کار با TextStream ها فصل دهم مقایسه انواع حالت های کامپایل در Qt نحوه افزودن دیگر کتابخانه های C++‎‎ در محیط Qt Creator و استفاده همراه با کتابخانه Qt نحوه خروجی گرفتن / گسترش (Deployment) در Qt مقایسه و پیکربندی دو موتور قدرتمند OpenGL و ANGLE در پروژه درایور دیتابیس هایی که تحت این کتابخانه پشتیبانی می‌شوند ساخت راه‌انداز دیتابیس در پلتفرم‌های Linux، macOS و Windows حق نشر کتاب و اهداف در نسخه‌ی بعدی کتاب توجه : در داشتن هر گونه انتقاد و پیشنهاد در رابطه با این کتاب با آدرس شخصی نویسنده (kambiz.ceo@gmail.com) مکاتبه نمایید. نکته : این کتاب در روز‌های خاص ممکن است شامل تخفیف قرار بگیرد. نکته دوم : کسانی که این کتاب را یک بار خریداری می‌کنند نسخه‌ی به روز رسانی شده آن را به صورت رایگان می‌توانند دریافت کنند.

    200٬000 ریال

  16. 1 امتیاز
    پردازنده‌ها چگونه طی ۴۰ سال گذشته تغییر کرده‌اند؟ پردازنده‌ها از پیدایش تا‌به‌حال، در‌حال‌پیشرفت بوده‌اند و روز‌به‌روز درکنار قدرتمند‌ترشدن، مصرف انرژی آن‌ها هم بهینه‌سازی شده است. اما این پیشرفت‌ها چقدر بوده و در آینده چگونه خواهد بود؟ وقتی از طرح‌های پیشرفت تکنولوژی، به‌ویژه قانون مور، صحبت به‌میان می‌آید، طرح «۳۵ سال از داده‌های ریزپردازنده‌ها» که آن را ام. هورویتز، اف. لابونت، اُ. شچم، کی. الوکتن، ال. هموند و سی. بَتِن جمع‌آوری کرده‌اند، می‌تواند یکی از طرح‌های مهم باشد. بعد‌ها، سی. مور هم اطلاعاتی به این پروژه اضافه کرد. این طرح را چه با خطوط پیشرفت و چه بدون آن‌ها می‌توان در جاهای مختلفی از اینترنت پیدا کرد؛ هر‌‌چند این طرح فقط تا سال ۲۰۱۰ کامل شده و در چند سال اخیر، کامل نشده است. برای به‌روزکردن داده‌های این طرح که هر‌چند درست‌بودن آن تا سال ۲۰۱۰ مشخص نیست، داده‌هایی از g3data و داده‌های دیگری هم از پردازنده‌های AMD Opteron، پردازنده‌های Intel Xeon، پردازنده‌های Power7+ و Power8 مانند Xeon Phi به این طرح اضافه شدند. جزئیات این داده‌های جدید را به‌صورت خام می‌توانید درون این فایل زیپ ببینید. نتیجه‌ی این طرح عکس زیر است: درادامه، طرح به‌روز‌شده را با طرح اصلی می‌توانید مقایسه کنید. نکته‌ای جالبی که وجود دارد، این است که باتوجه‌به اینکه عملکرد پردازش تک‌هسته‌ای ازنظر کمّیّت مهم است، این مقدار پیوسته در‌حال‌پیشرفت بوده است. این افزایش نتیجه‌ی مدیریت انرژی هوشمندانه و تنظیم دینامیک فرکانس کلاک (توربو) بوده است. در آینده، چه تغییراتی به وجود خواهد آمد؟ احتمالا فرکانس و انرژی مصرفی دستخوش تغییرات زیادی قرار نخواهند گرفت. بهبود بیشتر در ساختار کلاک ممکن است باعث افزایش تدریجی عملکرد تک‌هسته‌ای پردازنده‌ها شود که البته نمی‌توان انتظار تغییر بزرگی داشت. دو نمونه از کمّیّت‌های مهم، تعداد ترازیستور‌ها و تعداد هسته‌ها هستند. تا چه زمانی قانون مور ادامه خواهد داشت؟ این احتمال وجود دارد که در آینده‌ای نزدیک، افزایشی در تعداد هسته‌ها را شاهد خواهیم بود؛ اما شاید تعداد ترانزیستور‌ها تغییری اساسی نکنند. در‌حال‌حاضر، Haswell Xeon در صدر فهرست پردازنده‌ها هستند که ۱۸ هسته‌ی پردازشی دارند. به‌هرحال با وجود این پردازنده‌ها، قانون امدال ما‌ را به‌ دنبال‌کردن همین الگوریتم ملزم خواهد کرد. پردازندهی Knight Landing Xeon Phis که به‌زودی رونمایی خواهد شد، ۷۲ هسته دارد که بیش از ۶۱ هسته بیشتر از نسل کنونی‌اش خواهد داشت. از دیدگاه الگوریتم‌ها، واقعا مهم نیست پردارنده با ۶۱ یا ۷۲ هسته کار می‌کند یا خیر؛ بلکه در هر دو مورد، الگوریتم‌هایی موازی موردنیاز هستند. در این مرحله، باید خوشحال باشیم که در‌حال‌حاضر، توانسته‌ایم با یادگیری برنامه‌ریزی GPU‌ها این الگوریتم‌ها را طراحی و اجرا کنیم. به‌روزرسانی ۲۰۱۸ دو سال داده‌ی بیشتر به‌نظر مهم نیست، هرچند به‌نظر می‌رسد قانون مور در‌حال‌ کم‌رنگ‌شدن است. یکی از موضوعاتی که باید به آن اشاره کرد، این است که اینتل دیگر تعداد ترانزیستور‌های پردازنده‌های خود را اعلام نمی‌کند. همچنین، تعدادی از پردازنده‌های این شرکت زمان زیادی بعد از موعد مقرر معرفی شدند. مدل Tick-Tock هم اصلاح شده است. با داده‌هایی از تعداد ترانزیستور‌ها که از AMD Epyc و IBM Power 9 به‌دست‌آمده طرح را به‌صورت زیر به‌روزرسانی کرده‌اند: واضح است تعداد ترانزیستور‌ها به‌صورت نموداری نمایی رو‌به‌پیشرفت بوده است. تا‌به‌امروز، پردازنده‌ی AMD Epyc با ۱۹،۰۰۰،۰۰۰،۰۰۰ ترانزیستور که به‌صورت عمومی اعلام شده، بیشترین تعداد ترانزیستور را در میان پردازنده‌ها دارد. برای مقایسه باید گفت تراشه‌ی پاسکال Nvidia GP100 درحدود ۱۵،۰۰۰،۰۰۰،۰۰۰ ترانزیستور دارد. با درنظرگرفتن این تعداد، این ارقام باهم سازگار هستند و جای شکی در تعداد ترانزیستور‌ها وجود ندارد.به‌زودی، با معرفی نود‌های پردازشی ۱۰ نانومتری منطقی است که احتمال دهیم تا چند سال آینده، منحنی نمایی و رو‌به‌رشد تعداد ترانزیستور‌ها پیشرفت خود را حفظ کند. تعداد ترانزیستور بیشتر موجب افزایش تعداد هسته‌ها می‌شود. این درحالی است که پیشرفتی که در SpecINT برای محاسبه عملکرد تک‌هسته‌ای قابل مشاهده‌است، مستقیما نتیجه‌ی استفاده از کامپایلر‌های Auto-Vectorization و Auto-Parallelization است.
  17. 1 امتیاز
    در این مقاله من قصد دارم در رابطه با تفاوت‌های اختصاص دادن حافظه در اِستَک و هیپ توضیحاتی دهم که بسیاری از علاقه‌مندان راجع به آن‌ها سوال کرده‌اند. با توجه به اینکه، از اوایل سیستم‌های کامپیوتری این تمایز در این وجود داشته است که برنامه های اصلی در حافظه فقط خواندنی مانند ROM ، PROM و یا EEPROM نگه داری می‌شوند. به عنوان دیگر از زمانی که سیستم ها پیچیده‌تر شدند برنامه‌ها از حافظه‌های دیگری مانند RAM به جای اجرا در حافظه ROM استفاده کردند. این ایده به خاطر این بود که تعدادی از قسمت های حافظه مربوط به برنامه نباید تغییر یابند و در این حالت باید حفظ شوند. در این میان دو بخش .text و .rodata بخشی‌هایی از برنامه هستند که می‌تواند به بخش های دیگر برای وظایف خاص تقسیم شوند که در ادامه به آن‌ها اشاره شده است. بخش کد، به عنوان یک بخش متنی (.text) و یا به طور ساده به عنوان متن شناخته می‌شود. جایی است که بخشی از یک فایل شیء یا بخش مربوطه از فضای آدرس مجازی برنامه که حاوی دستورالعمل های اجرایی است و به طور کلی فقط خواندنی بوده و اندازه ثابتی دارد می‌باشد. بخش .bss که به عنوانی بخشی ویژه (محل نگه داری اطلاعات تخصیص داده نشده (مقدار دهی نشده)) محلی که متغیر‌های سراسری و ثابت با مقدار صفر شروع می‌شوند. بخش داده (.data) حاوی هر گونه متغیر سراسری و یا استاتیک که دارای یک مقدار از پیش تعریف شده هستند و می‌توانند اصلاح شوند. تصویر زیر طرح معمولی از یک حافظه برنامه ساده کامپیوتری را با متن، داده های مختلف، و بخش‌های استک و هیپ و bss را نشان می‌دهد. برای مثال در کد C به صورت زیر خواهد بود: int val = 3; char string[] = "Hello World"; مقادیر برای این نوع متغیر‌ها در ابتدا در حافظه فقط خواندنی ذخیره می‌شوند. (معمولا در داخل .text) و در زمان اجرای برنامه که به صورت روتین خواهد بود در بخش .data کپی می‌شوند. بخش BSS یا همان .BSS در برنامه‌نویسی کامپیوتر، نام .bss یا bss توسط بسیاری از کامپایلرها و لینکرها برای بخشی از دیتا سِگمنت (Data Segment) استفاده می‌شود که حاوی متغیر های استاتیک اختصاصی که تنها از بیت هایی با ارزش صفر شروع شده است می‌باشد. این بخش به عنوان BSS Section و یا BSS Segment شناخته می‌شود. به طور معمول فقط طول بخش bss نه data در فایل آبجکت ذخیره می‌شود. برای نمونه، یک متغیر به عنوان استاتیک تعریف شده است static int i; این در بخش BSS خواهد بود. حافظه هیپ (Heap) ناحیه‌ی هیپ (Heap) به طور رایج در ابتدای بخش‌های .bss و .data قرار گرفته است و به اندازه‌های آدرس بزرگتر قابل رشد است. ناحیه‌ی هیپ توسط توابع malloc, calloc, realloc و free مدیریت می‌شود که ممکن است توسط سیستم‌های brk و sbrk جهت تنظیم اندازه مورد استفاده قرار گیرد. ناحیه هیپ توسط تمامی نخ‌ها، کتابخانه‌های مشترک و ماژول‌های بارگذاری شده در یک فرآیند به اشتراک گذاشته می‌شود. به طور کلی حافطه Heap بخشی از حافظه کامپیوتر شما است که به صورت خودکار برای شما مدیریت نمی‌شود، و به صورت محکم و مطمئن توسط پردازنده مرکزی مدیریت نمی‌شود. آن بیشتر به عنوان یک ناحیه شناور بسیار بزرگی از حافظه است. برای اختصاص دادن حافظه در ناحیه هیپ شما باید از توابع malloc(), calloc() که توابعی از C هستند استفاده کنید. یکبار که شما حافظه ای را در ناحیه هیپ اختصاص دهید، جهت آزاد سازی آن باید خود مسئول باشید و با استفاده از تابع free() این کار را به صورت دستی جهت آزاد سازی حافظه اختصاص یافته شده انجام دهید. اگر شما در این کار موفق نباشید، برنامه شما در وضعیت نَشت حافظه (Memory Leak) قرار خواهد گرفت. این بدین معنی است که حافظه اختصاص یافته شده در هیپ هنوز خارح از دسترس قرار گرفته و مورد استفاده قرار نخواهد گرفت. این وضعیت همانند گرفتگی رَگ در بدن انسان است و حافظه نشت شده جهت عملیات در دسترس نخواهد بود. خوشبختانه ابزار‌هایی برای کمک کردن به شما در این زمینه موجود هستند که یکی از آن‌ها Valgrind نام دارد و شما می‌توانید در زمان اشکال زدائی از آن جهت تشخیص نواحی نشت دهنده حافظه استفاده کنید. بر خلاف حافظه اِستک (Stack) حافظه هیپ محدودیتی در اندازه متغیر‌ها ندارد (جدا از محدودیت آشکار فیزیکی در کامپیوتر شما). حافظه هیپ در خواندن کمی کُند تر از نوشتن نسبت به حافظه اِستک است، زیرا جهت دسترسی به آن‌ها در حافظه هیپ باید از اشاره گر استفاده شود. بر خلاف حافظه اِستک، متغیر‌هایی که در حافظه هیپ ساخته می‌شوند توسط هر تابعی در هر بخشی از برنامه شما در دسترس بوده و اساسا متغیر‌های تعریف شده در هیپ در دامنه سراسری قرار دارند. حافظه اِستک (Stack) ناحیه‌ی اِستک (Stack) شامل برنامه اِستک، با ساختار LIFO کوتاه‌ شده عبارت Last In First Out (آخرین ورودی از همه زودتر خارج می‌شود) به طور رایج در بالاترین بخش از حافظه قرار می‌گیرد. یک (اشاره گر پشته) در بالاترین قسمت اِستک قرار می‌گیرد. زمانی که تابعی فراخوانی می‌شود این تابع به همراه تمامی متغیرهای محلی خودش در داخل حافظه اِستک قرار می‌گیرد و با فراخوانی یک تابع جدید تابع جاری بر روی تابع قبلی قرار می‌گیرد و کار به همین صورت درباره دیگر توابع ادامه پیدا می‌کند. مزیت استفاده از حافظه اِستک در ذخیره متغیرها است، چرا که حافظه به صورت خودکار برای شما مدیریت می‌شود. شما نیازی برای اختصاص دادن حافظه به صورت دستی ندارید، یا نیازی به آزاد سازی حافظه ندارید. به طور کلی دلیل آن نیز این است که حافظه اِستک به اندازه کافی توسط پردازنده مرکزی بهینه و سازماندهی می‌شود. بنابراین خواندن و نوشتن در حافظه اِستک بسیار سریع است. کلید درک حافظه اِستک در این است که زمانی که تابع خارج می‌شود، تمامی متغیر‌های موجود در آن همراه با آن خارج و به پایان زندگی خود میرسند. بنابراین متغیر‌های موجود در حافظه اِستک به طور طبیعی به صورت محلی هستند. این مرتبط با مفهوم دامنه متغیر‌ها است که قبلا از آن یاد شده است، یا همان متغیر‌های محلی در مقابل متغیر‌های سراسری. یک اشکال رایج در برنامه نویسی C تلاش برای دسترسی به یک متغیر که در حافظه اِستک برای یک تابع درونی ساخته شده است می‌باشد. یعنی از یک مکان در برنامه شما به خارج از تابع (یعنی زمانی که آن تابع خارج شده باشد) رجوع می‌کند. یکی دیگر از ویژگی‌های حافظه اِستک که بهتر است به یاد داشته باشید این است که، محدودیت اندازه (نسبت به نوع سیستم عامل متفاوت) است. این مورد در حافظه هیپ صدق نمی‌کند. خلاصه ای از حافظه اِستک (Stack) حافظه اِستک متناسب با ورود و خروج توابع و متغیر‌های درونی آن‌ها افزایش و کاهش می‌یابد نیازی برای مدیریت دستی حافظه برای شما وجود ندارد، حافظه به طور خودکار برای متغیر‌ها اختصاص و در زمان نیاز به صورت خودکر آزاد می‌شود در اِستک اندازه محدود است متغیر‌های اِستک تنها در زمان اجرای تابع ساخته می‌شوند مزایا و معایب حافظه اِستک و هیپ حافظه اِستک (Stack) دسترسی بسیار سریع به متغیر‌ها نیازی برای باز پس گیری حافظه اختصاص یافته شده ندارید فضا در زمان مورد نیاز به اندازه کافی توسط پردازنده مرکزی مدیریت می‌شود، حافظه ای نشت نخواهد کرد متغیر‌ها فقط محلی هستند محدودیت در حافظه اِستک بسته به نوع سیستم عامل متفاوت است متغیر‌ها نمی‌توانند تغییر اندازه دهند حافظه هیپ (Heap) متغیر‌ها به صورت سراسری قابل دسترس هستند محدودیتی در اندازه حافظه وجود ندارد تضمینی برای حافظه مصرفی وجود ندارد، ممکن است حافظه در زمان‌های خاص از برنامه نشت کرده و حافظه اختصاص یافته شده برای استفاده در عملیات دیگر آزاد نخواهد شد شما باید حافظه را مدیریت کنید، شما باید مسئولیت آزاد سازی حافظه های اختصاص یافته شده به متغیر‌ها را بر عهده بگیرید اندازه متغیر‌ها می‌تواند توسط تابع realloc() تغییر یابد در اینجا یک برنامه کوتاه وجود دارد که در آن متغیرها در یک حافظه اِستک ایجاد شده اند. #include <stdio.h> double multiplyByTwo (double input) { double twice = input * 2.0; return twice; } int main (int argc, char *argv[]) { int age = 30; double salary = 12345.67; double myList[3] = {1.2, 2.3, 3.4}; printf("double your salary is %.3f\n", multiplyByTwo(salary)); return 0; } ما متغیر‌هایی را اعلان کرده‌ایم که یک int، یک double و یک آرایه که سه نوع double دارد هستند. این متغیر‌ها داخل اِستک وارد و به زودی توسط تابع main در زمان اجرا حافظه مورد نیاز خود را دریافت خواهند کرد. زمانی که تابع main خارج می‌شود (برنامه متوقف می‌شود) این متغیر‌ها همگی از داخل حافظه اِستک خارج خواهند شد. به طور مشابه، در تابع multiplByTwo() متغیر twice که از نوع double است، داخل اِستک وارد شده و در زمان اجرای تابع multiplyByTwo() حافظه به آن اختصاص می‌یابد. زمانی که تابع فوق خارج شود یعنی به نقطه پایان اجرایی خود برسد، حافظه اختصاص یافته شده به متغیر‌های داخلی آن نیز آزاد خواهند شد. به طور خلاصه توجه داشته باشید که تمامی متغیر‌های محلی در این نوع تعریف تنها در طول زمان اجرایی زمانی تابع زنده هستند. به عنوان یک یادداشت جانبی، روشی برای نگه داری متغیر‌ها در حافظه اِستک وجود دارد، حتی در زمانی که تابع خارج می‌شود. آن روش توسط کلمه کلیدی static ممکن خواهد شد که در زمان اعلان متغیر استفاده می‌شود. متغیری که توسط کلمه کلیدی static تعریف می‌شود، بنابراین چیزی مانند متغیر از نوع سراسری خواهد بود، اما تنها در داخل تابعی که داخل آن ایجاد شده است قابل مشاهده خواهد بود. این یک ساختار عجیب و غریب است، که احتمالا به جز شرایط بسیار خاص نیازی به آن نباشد. نسخه‌ی دیگری از برنامه فوق در قالب حافظه هیپ به صورت زیر است: #include <stdio.h> #include <stdlib.h> double *multiplyByTwo (double *input) { double *twice = malloc(sizeof(double)); *twice = *input * 2.0; return twice; } int main (int argc, char *argv[]) { int *age = malloc(sizeof(int)); *age = 30; double *salary = malloc(sizeof(double)); *salary = 12345.67; double *myList = malloc(3 * sizeof(double)); myList[0] = 1.2; myList[1] = 2.3; myList[2] = 3.4; double *twiceSalary = multiplyByTwo(salary); printf("double your salary is %.3f\n", *twiceSalary); free(age); free(salary); free(myList); free(twiceSalary); return 0; } همانطور که می‌بینید، استفاده از malloc() برای تخصیص حافظه در حافظه Heap و استفاده از free() جهت آزاد سازی حافظه تخصیص یافته می‌باشد. این مواجه شدن چیز بسیار بزرگی محسوب نمی‌شود اما کمی مبهم است. چیز دیگری که باید به آن توجه داشته باشید علامت ستاره (*) است که در همه جای کد‌ها دیده می‌شود. اینها چه چیز‌هایی هستند؟ پاسخ این سوال این است : اینها اشاره گر هستند! توابع malloc() و calloc() و free() با اشاره‌گر‌هایی مواجه می‌شوند که مقادیرشان واقعی نیست. اشاره گر‌ها نوع داده ای خاصی در C هستند که آدرس حافظه مربوطه را بر می‌گردانند. در خط ۵ متغیر twice یک متغیر از نوع double نیست، اما اشاره به یک double دارد. آن آدرس حافظه ای است که نوع double در آن بلوک از حافظه ذخیره شده است. در ++C توسط کلمه کلیدی new که خود آن نیز یک اپراتور محسوب می‌شود می‌توان حافظه ای را در Heap اختصاص داد. به عنوان مثال: int* myInt = new int(256); آدرس‌های موجود در حافظه توسط اپراتور new به اشاره‌گر مربوطه پاس داده می‌شود. به مثال زیر توجه کنید، متغیر تعریف شده در حافظه اِستک قرار گرفته است: int variable = 256; سوالی که ممکن است افراد کنجکاو از خود بپرسند این است که چه زمانی از Stack و چه زمانی از Heap باید استفاده کنیم؟! خب پاسخ این سوال اینگونه خواهد بود، زمانی که شما نیاز به یک بلوک بسیار بزرگی از حافظه دارید، که در آن یک ساختار بزرگ یا یک ارایه بزرگی را ذخیره کنید و نیاز داشته باشید که متغیر‌های شما به مدت طولانی در سرتاسر برنامه شما در دسترس باشند در این صورت از حافظه Heap استفاده کنید. در صورتی که شما نیاز به متغیر‌های کوچکی دارید که تنها نیاز است در زمان اجرای تابع در دسترس باشند و قابلیت خواندن و نوشتن سریعتری داشته باشند از نوع حافظه Stack استفاده کنید. فقط فراموش نکنید که حافظه Heap تحت توابع molloc(), realloc(), calloc() و free() مدیریت می‌شوند. هرچند اشاره‌گر های هوشمند نیز در ++C وجود دارند اما در بسیاری از مواقع که نیاز است بسیار جزئی و حساس بر روی کد‌های خود کار کنید از مدیریت حافظه به صورت دستی استفاده کنید.
  18. 1 امتیاز
    نحوه ساخت یک کتابخانه (Shared Object (.so در سیستم عامل لینوکس توسط کامپایلر GCC مرحله 1: ابتدا باید در برنامه ای که قصد دارید آن را به کتابخانه عمومی تبدیل کنید باید یک رابط مناسب Factory Method برای لینک در زمان اجرا بسازید. نکته! توجه داشته باشید که می توانید با استفاده از پلی مورفیسم یک اشاره گر از یک رابط که به صورت مجرد تعریف شده است را در Factory Method کتابخانه خود انتقال دهید. تابع سازنده رابط کتابخانه را به شکل زیر در Main برنامه کتابخانه تعریف کنید...! #include "encryptorPlugin.h" extern "C" { extern PluginEnctyptorPtr create_plugin() { return PluginEnctyptorPtr(new encryptor_plugin); } extern bool destroy_plugin(PluginEnctyptorPtr instance) { if(instance !=NULL){ instance =NULL; delete instance; return true; } return false; } } int main(int argc, char *argv[]) { return 0; } مرحله 2: در این مرحله باید یک کلاس مجرد بسازید و متدهایی که باید زمان فراخوانی اشاره گر کلاس به آنها دسترسی داشته باشید را تعریف نمایید. مانند کلاس زیر که من تعریف کرده ام... #ifndef __PLUGIN_H__ #define __PLUGIN_H__ #include <sstream> #include <string.h> typedef char* Bytes; typedef unsigned int size_t; typedef const char* cBytes; #define DELOBJ(obj) {if(obj!=NULL){delete obj;obj=NULL;}} enum encryptAloglType { BASE64, HASH , MD4 ,MD5 ,OPENSSL }; enum ioType { CPP_STREAM , C_LOWLEVEL }; class Encryptor_Interface { public: virtual ~Encryptor_Interface() {} virtual void getEncBuffer(Bytes & buffer, cBytes fileName) const = 0; virtual void getDecBuffer(std::istringstream** sstream, cBytes fileName) const = 0; virtual void setIoType(ioType =C_LOWLEVEL) = 0; virtual void setAlgolType(encryptAloglType) = 0; virtual void dispose(int) =0; virtual size_t getReadedSize() =0; }; typedef Plugin_Interface* PluginEnctyptorPtr; #endif // __PLUGIN_H__ class ISCLVoterManager { public: virtual ~ISCLVoterManager(){}; virtual bool Init(int nSysID,int nNodeID,const char* strFileName) = 0; connections }; مرحله 3: در این مرحله بعد از پیاده سازی الگوریتم های متدهای کلاس مجرد ساخته شده باید سورس کتابخانه خود را در لینوکس کامپایل کنید. اگر توزیع لینوکس شما از bash استفاده میکند می توانید به روش زیر کامپایل را انجام دهید... gcc -ggdb3 -shared -fPIC libdep.c -o libEncryptor.so در این مرحله در صورتی که خطایی وجود نداشته باشد باید کتابخانه شما کامپایل شده باشد البته به صورت Shared Library. مرحله 4: اکنون در این مرحله کافی است که از رابط زیر که من به صورت template نوشته ام به صورتی که به اشاره خواهم کرد استفاده نمایید. در این کلاس کافی است که شما رابط مجرد کتابخانه را به صورت اشاره گر ویا در صورتی که متدهای شما در کتابخانه به آرگومانهایی نیاز دارند به همراه آرگومانهای کلاس را به سازنده این کلاس ارسال نمایید. در این کلاس با استفاده از تکنیک استراتژی شما می توانید انوع رابط های کتابخانه ها را به راحتی استفاده نمایید فقط کافی است که یک کلاس Wrapper بنویسید که کلاس مجرد Solver را به ارث برده باشد و در متد solve این کلاس نحوه پیاده سازی کتابخانه خود را بنویسید. زمانی که شما در پروژه های خیلی بزرگ کار میکنید که از چند ده کتابخانه مختلف استفاده میکنند به راحتی می توانید از این کلاس ژنریک استفاده نمایید تا هم اتصالات سخت را از بین برده باشید و هم کلاس های سبک رابطی را تهیه کرده اید که اصل Single Responsibility را هم به خوبی راعایت کرده باشید وهم کسانی که از رابط شما استفاده میکنند درگیر جزییات پیاده سازی نخواهند شد. /* * Library_Loader.h * * Created on: Jan 1, 2019 * Author: f.shiri */ #ifndef PLUGINLOADER_H_ #define PLUGINLOADER_H_ #include "Encryptor_interface.h" #include <stdexcept> #include <memory> #include <dlfcn.h> #define LibEncryptorName "./libEncryptor.so" #define LibFactoryMethod "create_plugin" #define LibDestyoryMethod "destroy_plugin" #define LogicFileName "logic.ini" #define __CleanupLibClose __attribute__((cleanup(closedl))) #ifdef SCL_EQUIPPED #include "SCLVoter_Interface.h" #include "initsclvoter.h" extern ISCLVoterManager* pSCLVoterManager; #endif typedef Encryptor_Interface* encAbstractPtr; //typedef ISCLVoterManager* pISCLVoter; /* load shared library or static library and dynamic * declare library virtual function. * template arg1 : interface class library pointer. * */ template<class InterfacePtr> class PluginLoader { private: /*clean up macro function calling by compiler, delete the pointer when the pointer's life cycle is end */ static inline int closedl(void* handle) { int tmp = -1; if (handle) tmp = dlclose(handle); handle = NULL; return tmp; } struct { /* union data type contained * dynamic link library objects. * */ union { void* dlsym; InterfacePtr (*factoryMethod)(); } factoryObject; union { void* dlsym; bool (*destoryMethod)(InterfacePtr); } destroyObject; /* shared library handler */ } libraryObject; void* m_handle; public: explicit PluginLoader() { } ; ~PluginLoader() { } ; // Dynamic load, encryption function from shared library. /* * arg1 : shared library name. * arg2 : reference factory method name in shared library. * arg3 : reference declarative interface class pointer. * arg4 : own request for the symbol in library or main executable. // arg5 : reference destroy method name in shared library. * return type : reference interface pointer. */ template<class CInterfacePtr> inline void operator()(cBytes libName, cBytes factoryFuncName, CInterfacePtr** pClassInterface,cBytes destroyFuncName =NULL) { dlerror(); /* * The RTLD_DEEPBIND attribute specifies that when resolving a symbol in a shared library, * the symbols in the shared library are put ahead of the global symbol scope. * example-> When you load the ???.so, it has it's own request for the symbol. * Because you use RTLD_DEEPBIND this definition is looked up in the dependencies of this library first, * before looking in the main executable. */ __CleanupLibClose void* m_handle = dlopen(libName, RTLD_DEEPBIND | RTLD_NOW | RTLD_LOCAL); if (!m_handle) { throw std::runtime_error(dlerror()); } if(factoryFuncName!=NULL){ if (!(libraryObject.factoryObject.dlsym = dlsym(m_handle, factoryFuncName))) { throw std::runtime_error(dlerror()); } } if(destroyFuncName!=NULL){ if (!(libraryObject.destroyObject.dlsym = dlsym(m_handle, destroyFuncName))) { throw std::runtime_error(dlerror()); } } *pClassInterface =(*libraryObject.factoryObject.factoryMethod)(); } template<class CInterfacePtr> bool destroy(CInterfacePtr** pClassInterface) { return libraryObject.destroyObject.destoryMethod(*pClassInterface); } private: PluginLoader(const PluginLoader&); PluginLoader& operator=(const PluginLoader&); }; /* this class is invoke strategy shared library wrapper * template arg1: reference interface class */ template<class InterfacePtr> class Solver { public: Solver(){}; Solver(cBytes cLibName, cBytes cFactoryFuncName ,cBytes cDestroyFuncName=NULL) :m_interfacePtr(NULL){ m_pluginLoader(cLibName, cFactoryFuncName, &m_interfacePtr ,cDestroyFuncName); } virtual ~Solver(){ m_pluginLoader.destroy(&m_interfacePtr); DELOBJ(m_interfacePtr); } // important: this pure virtual method must be implemented in derived class. virtual void solve() =0; protected: template<class CInterfacePtr> inline void getInterface(CInterfacePtr& inputInterFace){ if(m_interfacePtr!=NULL){ inputInterFace = m_interfacePtr; } } private: //shared library interface class member. InterfacePtr m_interfacePtr; //instance of plugin loader class. PluginLoader<InterfacePtr> m_pluginLoader; }; /*proxy class shared library. this class is immutable type * class type: pointer type of shared library interface class. * template arg1 : encrypt interface class pointer * template arg2 : string stream reference pointer * */ template<class InterfacePtr ,typename InputType> class EncryptLibraryLoader : public Solver<InterfacePtr>{ protected: public: // arg1 : shared library name. // arg2 : reference factory method name in shared library. // arg3 : reference destroy method name in shared library. // arg4 : output string stream EncryptLibraryLoader(InputType outStream ,cBytes cLibName, cBytes cFactoryFuncName ,cBytes cDestroyFuncName=NULL) : Solver<InterfacePtr>(cLibName,cFactoryFuncName,cDestroyFuncName) , m_outStream(outStream) { /* important: must be call getInterface in super class and get * interface class reference and copy in member pointer in this class. */ getInterface(m_interfacePtr); } virtual ~EncryptLibraryLoader() { //clean up member pointer DELOBJ(m_interfacePtr); DELOBJ(m_outStream); } /* get the byte stream from encrypt logic.ini file. * arg1 : reference bytes stream. * return type : reference stream pointer * * important: must be call getInterface in super class and get * interface class reference and copy in member pointer in this class. * */ inline void solve(){ if(m_outStream!=NULL){ try { if (m_interfacePtr!=NULL) { m_interfacePtr->setIoType(C_LOWLEVEL); m_interfacePtr->getDecBuffer(m_outStream,const_cast<char*> (LogicFileName)); } //important : if interfacePtr is loaded. // this method dispossess member static pointer in memory section of library. m_interfacePtr->dispose(0); } catch (std::runtime_error& e) { printf("%s \n", e.what()); } } } private: //EncryptLibraryLoader(); EncryptLibraryLoader(const EncryptLibraryLoader&); EncryptLibraryLoader& operator=(const EncryptLibraryLoader&); //shared library interface class member. InterfacePtr m_interfacePtr; InputType m_outStream; }; /*proxy class SclVoter library. this class is immutable type * class type: pointer type of shared library interface class. * template arg1 : SclVoter interface class pointer * template arg2 : Number of nodes with type int * */ template<class InterfacePtr,typename InputType> class SclVoterLibraryLoader : public Solver<InterfacePtr>{ protected: public: // arg1 : shared library name. // arg2 : reference factory method name in shared library. // arg3 : reference destroy method name in shared library. // arg4 : numbers of node explicit SclVoterLibraryLoader(InputType nNode ,cBytes cLibName, cBytes cFactoryFuncName, cBytes cDestroyFuncName=NULL) : Solver<InterfacePtr>(cLibName,cFactoryFuncName,cDestroyFuncName), m_x_nNode(nNode){ } virtual ~SclVoterLibraryLoader() { //clean up member pointer DELOBJ(m_interfacePtr); } /* initializing SCLVoter */ inline void solve(){ /* important: must be call getInterface in super class and get * interface class reference and copy in member pointer in this class. */ getInterface(m_interfacePtr); if(m_interfacePtr!=NULL){ char strConfigPath[100]; copy(strConfigPath,m_x_nNode); if(!m_interfacePtr->Init(strConfigPath)) { printf("Error initializing SCLVoter (file %s) !\n",strConfigPath); } } } private: SclVoterLibraryLoader(); SclVoterLibraryLoader(const SclVoterLibraryLoader&); SclVoterLibraryLoader& operator=(const SclVoterLibraryLoader&); //shared library interface class member. InterfacePtr m_interfacePtr; InputType m_x_nNode; }; #endif /* PLUGINLOADER_H_ */ و در این مرحله به استفاده از آبجکت های زیر از کتابخانه های خود استفاده نمایید. std::istringstream* file=NULL ; { Solver<encAbstractPtr> *loader = new EncryptLibraryLoader<encAbstractPtr,std::istringstream**> ( &file ,LibEncryptorName, LibFactoryMethod , LibDestyoryMethod); // initial encryption library. loader->solve(); loader=NULL; } Solver<ICLAbstractPtr> *loader = new SclVoterLibraryLoader<ICLAbstractPtr,int> ( m_nNodeId ,SCLVOTER_SO_PATH, SCLVOTER_GET_INSTANCE_FUNCTION_NAME ); if(pSCLVoterManager == NULL) { return false; } // initial SCLvoter. loader->solve(); loader=NULL; در صورتی که نیاز به استفاده از کتابخانه در ویژوال سی ++ بود در همین تاپیک اعلام کنید تا انجام بدم.
  19. 1 امتیاز
    آیا این واقعاً امکان‌پذیر است؟ پاسخ : بله! من می‌دانم که ممکن است این مبحث تحت سی++ بسیار پیچیده و یک کار بیهوده‌ای باشد! اما واقعیت این است که تکنیک‌های پنهان بسیاری وجود دارد که ممکن است همه از آن باخبر نباشند! من قبلاً در مورد اینکه تحت ++C وب‌سایت میشه طراحی کرد یا خیر تحقیقاتی انجام داده بودم، از لحاظ امکان بودنش جواب مثبت بود اما اینکه به راحتی طراحی تحت Php یا دیگر زبان‌های برنامه‌نویسی باشه خیر! خُب طبیعیه چون شما بسیار راحت یه اسکریپت رو می‌نویسی و روی سرور اجراش می‌کنی و سایت شما به خوبی و خوشی بالا میاد! ممکن است در همین قسمت از موضوع شما به این نتیجه رسیده باشید که خُب نیازی به ادامه‌ی بحث نداریم! وقتی کار سختیه پس منطقی نیست و شما احتمالاً دیوانه‌ای!!! واقعیت جریان این است بر خلاف آن چیزی که تصور کرده‌ایم طراحی وب‌سایت با سی‌پلاس‌پلاس نه تنها بسیار راحت است بلکه بسیار هم جذاب خواهد بود! اما در نگاه اول ممکن است یک سری محدودیت‌هارا داشته باشد که همه‌ی این موارد با کمی تعمل و بررسی قابل حل هستند به قدری که وقتی درگیر این جریان باشید شیفته‌ی آن خواهید شد. مزایای یک وب‌سایت تحت سی‌پلاس‌پلاس نسبت به دیگر زبان‌های رایج سرعت خارق‌العاده و غیر قابل مقایسه با زبان‌های رایج امنیت بهتر کد‌های شما مدیریت ساده‌تر و انعطاف‌پذیری بالا مصرف بسیار بهینه‌ و غیر قابل تصور از منابع سرور دسترسی نامحدود به کتابخانه‌ها عدم محدودیت در دسترسی به برنامه‌نویسی سطح پایین عدم محدودیت در استفاده از توابع سیستم‌عامل عدم محدودیت در مدیریت سیستم و هر ویژگی‌ دیگری که در زبان‌های اسکریپتی اگر به آن نیاز داشته باشید مجبور هستید تا به صورت اکستنشن آن را تحت سی‌پلاس‌پلاس باز نویسی کنید. سیستم راه‌انداز وب‌سرور چگونه است؟ در هر سروری CGI به شما امکان این را می‌دهد که بتوانید تحت پروتکل‌های استاندارد برنامه‌های تحت وب را اجرا کنید. شما می‌توانید تحت آن و یا موارد دیگری مانند FastCGI و WSGI و دیگر موارد بهینه شده‌ی آن برنامه‌ی تحت وب را بر روی سرور خود اجرا کنید. طراحی قالب هماهنگی با HTML, JavaScript, Css در سی‌پلاس‌پلاس چگونه خواهد بود؟ همه‌ی گزینه‌های مربوط به وب را شما بدون هیچ محدودیتی در اختیار خواهید داشت. شما هیچ محدودیتی در استفاده از ویژگی‌های HTML5 یا CSS3 و یا JavaScript و دیگر فریمورک‌ها و کتابخانه‌های کارآمدی چون Angular.JS را نخواهید داشت. بنابراین از نظر طراحی رابط یک وب‌سایت همانند دیگر زبان‌های رایج می‌توانید روی آن حساب کنید. طراحی هسته و بک‌اِند وب‌سایت چگونه خواهد بود؟ همانند زبان‌ها و فریمورک‌های رایج تحت وب شما در سی‌پلاس‌پلاس می‌توانید هسته‌ی وب‌سایت یا سیستم وب‌سایت خود را تحت استاندارد سی‌پلاس‌پلاس و هر کتابخانه‌ای که می‌پسندید و یا به آن تسلط دارید پیاده سازی کنید! به شرطی که قابلیت‌های آن کتابخانه پاسخگوی نیاز‌های شما باشد. با این حساب شما می‌توانید حتی سیستم مدیریت محتوای (CMS) خود را طراحی کنید! بله سیستم مدیریت محتوا تحت سی‌پلاس‌پلاس! کاملاً جدی هستیم قبل از هر چیز یک مزیت بسیار بزرگ در کنار مزیت‌های دیگر این است که یک CMS تحت سی‌پلاس‌پلاس می‌تواند داشته باشد مصرف بهینه از منابع سرور خواهد بود. برای مثال در یک مقایسه‌ی‌ ساده و آزمایشی نتیجه‌ی بسیار جالبی ارائه شده است. همانطور که می‌دانید Wordpress به عنوان یک سیستم مدیریت محتوای (بلاگ) شناخته شده و تحت Php توسعه‌ یافته است. نسخه‌ی سریعتر و بهینه‌تر آن با نام Ghost تحت Node.JS توسعه یافته است که ما نسخه‌ی توسعه‌ یافته‌ی آن را با یک عمل مشابه در C++1z مورد بررسی قرار داده ایم که نتایج آن بسیار جالب است! مصرف حافظه‌ سیستم مدیریت محتوای Tegra ۳۵۰۰ درخواست در هر ثانیه 3.6 مگابایت سیستم مدیریت محتوای Ghost 100 درخواست در ثانیه 120 مگابایت پشتیبانی از پایگاه‌های داده به لطف کتابخانه‌های عظیم سی‌پلاس‌پلاس امکان مدیریت یک وب‌سایت تحت پایگاه‌های داده مختلفی ممکن است. برای مثال تحت Qt شما می‌توانید به رایجترین درایور‌های بانک‌اطلاعاتی دسترسی داشته و سیستم خود را به آن‌ها مجهز کنید. نکته: احتمالاً در برنامه‌نویسی با نود جی‌اس و پی‌اچ‌پی شناختی با کتابخانه‌های OpenSSL, Libcurl و موارد این چنینی داشته اید! کتابخانه‌های فوق عضو لیست کتابخانه‌های C و ++C هستند. بنابراین شما علاوه بر دسترسی مستقیم بر آن‌ها به هزاران و شاید میلیون‌ها کتابخانه در دنیا سی‌پلاس‌پلاس خواهید داشت. نمونه‌ی اولیه اما شوق‌آور برای اثبات امکان طراحی وب‌سایت تحت سی‌پلاس‌پلاس چندی پیش من تصمیم گرفتم تا سیستم وب‌سایتی را تحت Php7 برای یکی از استارت‌آپ‌ها طراحی و پیاده سازی کنم که در این پست به آن اشاره شده است. از آن‌جایی که به لطف کتابخانه‌ی Qt برنامه‌های سمت کاربر را توسط سی‌پلاس‌پلاس پیاده سازی کرده بودم به این فکر افتادم چرا سمت سرور و بخش وب‌سایت هم با آن هماهنگ نشود!؟ اینگونه هماهنگی بین برنامه‌ها و پرفرمنس همه‌ی آن‌ها بسیار افزایش خواهد یافت در اولین نگاه این تفکر بسیار ناشیانه و بسیار ناممکن بود! تنها روشی که به کار گرفته بودم ارسال اطلاعات از طرف کاربر به سمت سرور و مدیریت آن‌ها تحت معماری Restful Api بود که در قالب JSon آن‌ها را تجزیه و مدیریت می‌کردم. با کمی تحقیق در مورد ویژگی‌های سمت وب تحت Fast-CGI, uWSGI, DJango, ClearSilver و موارد مرتبط با آن‌ها سعی کردم تا صفحه‌ی بسیار ساده‌ای از HTML را توسط سی‌پلاس‌پلاس هندل کنم. این کار نتایج بسیار موفقیت آمیزی را در بر داشت تا نتیجه‌ی آن تبدیل به یک پروژه‌ی سیستم مدیریت محتوا تحت ++C شد. من پروژه‌ای با نام مفهومی Tegra که نام پروژه‌ی قبلی تحت Php بود را در محیط Qt Creator با C++17 و کتابخانه‌ی Qt باز سازی کرده و هسته‌ی اولیه‌ی آن را برای اجرای چند صفحه از یک وب‌سایت، احراز هویت، بازخوانی و نمایش لیستی از خبر‌ها و مدیریت متا تگ‌ها و آدرس صفحات مربوط به هر صفحه را ایجاد کردم. سعی کرده ام در کمترین زمان ممکن برای آزمایش یک سری ویژگی‌های اولیه از یک وب‌سایت آن‌ها را مورد بررسی قرار بدم که عبارتند از هماهنگی با فریم‌ورک‌های طراحی مانند BootStrap و یا Angular.JS که خوشبختانه همه‌چیز بسیار خوب در کنار همدیگه کار می‌کنند. هسته‌ی سیستم به صورت جدا و معماری طراحی آن بر پایه‌ی MVC مورد آزمایش قرار گرفته است. در زیر تصاویری از صفحات تولید شده تحت سیستم‌ مدیریت محتوای ساخته شده با سی‌پلاس‌پلاس را مشاهده می‌کنید. همه چیز در قدم‌های اول قرار دارد و با توجه به سادگی تولید وب سایت بر خلاف تصوری که داشتیم بسیار توسعه و جای پیشرفت خواهد داشت. بخشی از نمونه کد‌های این سیستم به صورت زیر آورده شده است تا ذهنیتی برای توسعه‌دهندگان ارائه شود: تکه کُد زیر عمل ارسال اطلاعات و تمامی لینک‌های مربوط به بوت استرپ را برای سمت HTML ارائه می‌کند که در قالب استاندارد جدید C++17 آورده شده است: auto bootstrapCss = bootStrapLib.find("css"); if(bootstrapCss != bootStrapLib.end()) { c->setStash("BootstrapCss", bootstrapCss->second.c_str()); std::cout << "Found " << bootstrapCss->first << " " << bootstrapCss->second << '\n'; } کد مربوط به سمت قالب به صورت زیر خواهد بود: <!-- Bootstrap core css --> <link href="{{BootstrapCss}}" rel="stylesheet"> نتیجه‌ی فوق در صورتی که CDN بر روی لوکال تنظیم شده باشد از روی کد‌های کامپایل شده و یا استاتیک فراخوانی خواهد شد. در غیر اینصورت از روی یکی از سرور‌های CDN فراخوانی می‌شوند. نحوه‌ی ارسال متغیر از سمت سی‌پلاس‌پلاس به قالب بسیار ساده است! بسیار ساده از Php و یا Node.JS می‌باشد. با در نظر گرفتن ارسال اطلاعات از سمت سی‌پلاس‌پلاس به سمت رابط کاربری کافی است نام متغیر‌ها را در قالب خود اعمال کنید. {% for post in news %} <div class="blog-post"> <h2 class="blog-post-title"><a href="news/{{post.uri}}">{{post.title}}</a></h2> <p class="blog-post-meta">{{post.date}} by <a href="#">{{post.author}}</a></p> <p>{{post.announcement|safe}}</p> </div><!-- /.blog-post --> {% endfor %}</div> این ساختار بر پایه‌ی ساختار Angular.JS و DJango پیاده سازی شده است که به طور کامل پشتیبانی می‌شود. فعال سازی فناوری Angular.JS بر روی این سیستم جهت طراحی قالب تنها با دو دستور ساده اعمال می‌شود: <!-- Link to AngularJS --> <script src= "{{AngularJs}}"></script> <!-- Enable AngularJS Engine --> {{AngularJsSync|safe}} این دستورات در هسته‌ی سیستم مدیریت محتوا در کلاسی با نام Template پردازش و در نهایت به سمت HTML هندل می‌شوند. بخشی از دستورات سمت هسته در سی‌پلاس‌پلاس ۱۷ برای مثال ارسال عنوان صفحه به صورت زیر است: std::optional<std::string> LoadListTemplate::getTitle() const { if (isset(title)) { return title; } else { return std::nullopt; } } سمت HTML کافی است دستور فوق را در نظر بگیریم: <title>{{title}}</title> این‌ها مثال‌هایی از مراحل توسعه‌ی این سیستم است که می‌دانم آنچنان گسترده نیست، اما برای ثابت کردن طراحی و توسعه‌ی وب‌سایت تحت سی‌پلاس‌پلاس مثال‌های روشنی هستند. موفق و سربلند باشید! اطلاعیه‌های مربوط به این پروژه احتمالاً در کانال‌ها و گروه‌ تلگرامی و همین مرجع بازگو و در اختیار شما قرار گیرد.
  20. 1 امتیاز
    ادامه آموزش توابع کاربردی برای برنامه نویسی سوکت... تابع bind : int bind( SOCKET s, const struct sockaddr FAR *name, int namelen ); این تابع وظیفه پیوند دادن اطلاعات ارتباطی (مانند آدرس) را به سوکت تعریف شده دارد ودر برنامه سرور به کار می رود. آرگومان های این تابع به ترتیب زیر خواهد بود... آرگومان اول که متغیر نوع سوکت تعریف شده در برنامه است که با مقدار برگشتی تابع socket مقدار دهی شده است. آرگومان دوم آدرس محلی از حافظه است که متغیر ساختمان sockaddr_in در آنجا تعریف شده است (شرح این ساختمان در ادامه درج شده است.) آرگومان سوم هم اندازه متغیر ساختمان sockaddr_in است که می توانید با عملگر sizeof اندازه آن را بدست آورید. این تابع در صورت موفقیت مقدار صفر را بر می گرداند و در صورت عدم موفقیت SOCKET_ERROR را بر می گرداند. ساختمان داده ای sockAddr_in یک structure تعریف شده در هدر winsock.h است که محل نگه داری شماره پورت ارتباط و همچنین آدرس IP و دیگر اطلاعات است. struct sockaddr_in{ short sin_family, unsigned short sin_port, struct in_addr sin_addr, char sin_zero[8] }; عضوی به نام sin_family برای نگه داری نوع آدرس ارتباطی است که برای کاربرد های اینترنتی برابر ثابت AF_INET است. عضو sin_port مشخص کننده شماره پورت ارتباطی است. تابع تبدیل کننده htons(int portNumer) برای تبدیل عدد به نوع قابل فهم (BE1 to LE2) توسط ساختمان از تابع فوق استفاده می شود. و عضو sin_addr که خود یک ساختمان می باشد برای نگه داری آدرس IP که به صورت یک رشته کاراکتر مشخص می شود می باشد تابع تبدیل کننده inet_addr(char *ipaddress) برای تبدیل رشته کاراکتری که معرف آدرس IP است به نوع قابل فهم برای ساختمان داده از تابع فوق استفاده می کنیم. مثال کاربردی : sockaddr_in recSinIP; recSinIP.sin_family = AF_INET ;//set address family recSinIP.sin_port = htons(1362);//set port number recSinIP.sin_addr.S_un.s_addr = inet_addr("192.168.0.1"); نکته فنی : توجه داشته باشید که طبق تعاریف شبکه مقدار حداکثری تعریف پورت می تواند 65555 می باشد. که از این مقدار 1024 پورت اول توسط لایه های شبکه رزرو شده اند بنابراین شما نمی توانید از آنها استفاده نمایید. مانند پورت 80 که مختص به پروتکل HTTP می باشد. تابع listen : int listen( SOCKET s, int backlog ); تابع listen وظیفه گوش دادن به خط را در برنامه سرور به عهده دارد توجه داشته باشید که این تابع فقط در برنامه سرور کاربرد دارد. شرح آرگومان ها: آرگومان اول S که متغیر سوکت تعریف شده در برنامه می باشد. آرگومان دوم backlog هم تعداد صف های درخواستی است که به سیستم در یک زمان درخواست ارتباط داده اند و سیستم می تواند آنها را به حالت معلق نگه دارد این آرگومان می تواند برابر ثابت SOMAXCONN باشد. این تابع در صورت موفقیت صفر و درصورت بروز خطا ثابتی برابر با SOCKET_ERROR را برمی گرداند. تابع accept : SOCKET accept( SOCKET s, struct sockaddr FAR *addr, int FAR *addrlen ); این تابع برای قبول درخواست اتصال بعد از تابع listen استفاده می شود. آرگومان های این تابع مانند تابع bind می باشد با این تفاوت که در آرگومان addr مشخصات ماشین متصل شده به سرور قرار می گیرد. آرگومان addrlen اندازه ساختمانی که این اطلاعات را نگهداری می کنند را مشخص می کند. این تابع در صورت موفقیت مقدار برگشتیش توصیفات مربوط به سوکت ماشین راه دور مورد نظر ماست و در صورت بروز خطا ثابتی به نام INVALID_SOCKET را بر می گرداند. تابع connect : int connect( SOCKET s, const struct sockaddr FAR *name, int namelen ); تابع connect وظیفه برقراری ارتباط را با برنامه سرور بر عهده دارد و آرگومان های آن به شرح زیر می باشد: آرگومان اول s که از نوع سوکت می باشد وهمان مقدار برگشتی تابع socket می باشد. آرگومان دوم هم که از نوع sockAddr_in می باشد. و آرگومان آخر تابع connect یعنی namelen اندازه ساختمان داده sockaddr_in است که می توان به جا مقدار آن نوشت : sizeof(sockaddr_in) این تابع در صورت موفقیت مقدار صفر و در صورت بروز خطا ثابتی برابر با SOCKET_ERROR را بر می گرداند. نکته فنی : همانطور که میدانید در کامپیوتر های امروزی کلمات در حافظه به صورت (LE(Little Endian ذخیره می شوند، یعنی بایت کم ارزش تر در خانه با شماره کوچکتر ذخیره می شود و بایت با ارزشتر در خانه با شماره بزرگتر ذخیره می شود. مثلا برای عدد هگز 3578H بایت کم ارزشتر برابر 78 وبایت پرارزشتر برابر عدد 35 است وترتیب قرار گرفتن آنها در حافظه به صورت زیر می باشد: خانه 89:35 خانه 89:34 خانه 89:33 خانه 89:32 همانطور که مشاهده میکنید بایت کم ارزشتر در آدرس 89:33 ذخیره شده و بایت با ارزشتر در خانه 89:34 ذخیره شده است کامپیوترهای معمول بدین صورت کلمات را در حافظه ذخیره میکنند اما در پشته TCP/IP به صورت عکس عمل می شوند یعنی از نوع (BE(Big Endian پشتیبانی می شود به بیان دقیق تر باید ترتیب بایت ها در حافظه درست باشد یعنی بایت با ارزشتر در آدرس کوچکتر و بایت کم ارزشتر در آدرس بزرگترذخیره شود مثلا برای عدد 3578H مانند شکل زیر عمل می شود: خانه 89:32 خانه 89:33 خانه 89:34 خانه 89:35 البته باید توجه داشته باشید که فقط ما باید ورودی های هدر TCP/IP را به نوع (BE(Big Endian تبدیل کنیم یعنی آدرس IP و شماره پورت وکلا اطلاعاتی که تحت شبکه ارسال می شوند. دیگر ثوابت به طور خودکار در تمامی کامپیوترها به صورت صحیح قرار دارند و دیگری نیازی به تنظیم ما ندارند. //برمی گرداند BE این تابع آرگومان خود را که یک عدد دوبایتی است به حالت u_short htons( u_short hostshort ); //برمی گرداند BE این تابع آرگومان خود را که یک عدد چهار بایتی است را به حالت u_long htonl( u_long hostlong ); //برمی گرداند LE این تابع آرگومان خود را که یک عدد دوبایتی است به حالت u_short ntohs( u_short netshort ); //برمی گرداند LE این تابع آرگومان خود را که یک عدد چهار بایتی است را به حالت u_long ntohl( u_long netlong ); //که معرف آدرس آی پی است را دریافت میکند ویک عدد چهار بایتی (a.b.c.d)این تابع یک رشته کاراکتر //بر می گرداند در صورتی که آدرس آی پی نامعتبر باشد یا تابع عملیات خود را با موقیت انجام BE از نوع //توسط تابع برگردانده می شود INADDR_NONE ندهد ثابت unsigned long int inet_addr( const char FAR *cp ) همچنان ادامه خواهد داشت...
  21. 1 امتیاز
    با سلام هدف من از تهیه این آموزش شناخت کلی درباره (Socket Programming) که بسیار وسیع هست، والبته برای خیلی از برنامه نویسها هم همیشه جذاب بوده که بتوانند برنامه های تحت شبکه بنویسند بوده است. و از آنجاییکه با فراگیری برنامه نویسی سوکت در زبان C قادر خواهید بود، برنامه هایی در زمینه های بسیار تخصصی مانند (Intrusion Detect system) سیستم های تست نفوذ، ابزارهای (sniff) تحت نظرقرار دادن پکت ها، تا برنامه های ساده ای مانند چت و نمایش فیلم های آنلاین و خرید های مجازی و ... را انجام دهید. البته ما دراین آموزش سعی بر آن خواهیم داشت که بیشتر به نکات کاربردی درباره سوکت، که اغلب خود من از آنها در پروژه هایم استفاده کرده ام اشاره داشته باشیم. سوکت (Socket) چیست؟ با یک بیان ساده می توان گفت که سوکت به ترتیب یک آدرس ماشین IP ویک شماره درگاه Port گفته می شود. در برقراری ارتباط بین کامپیوترها در یک شبکه دوچیز بسیار مهم است. 1- آدرس ماشینی که می خواهیم اطلاعاتی از آن بگیریم ویا به آن ارسال کنیم. 2- برنامه ای از آن ماشین که در خواست اطلاعات کرده یا اینکه می خواهیم اطلاعاتی از آن برنامه کسب کنیم این دو یعنی آدرس ماشین وشماره برنامه به وسیله سوکت در شبکه مشخص می شوند. WinSock چیست؟ Winsock یا Windows Socket یک رویه (Interface) برنامه نویسی است که در غالب یک DLL در سیستم عامل ویندوز برای برنامه نویسی شبکه و ساخت برنامه هایی که بتوانند با شبکه محاوره داشته باشند معرفی شده است. ارتباط مابین دوکامپیوتر می تواند به یکی از دوصورت زیر باشد: - اتصال گرا (Connection Oriented) - غیر اتصال گرا (Connection Less) در سیستم اتصال گرا ابتدا درخواست اتصال ارسال شده و درصورت موافقت طرف مقابل ارتباط برقرار می شود.به این تکنیک Data Stream نیز گفته می شود. اما در سیستم غیر اتصال گرا بدون نیاز به موافقت طرف مقابل بسته ها ارسال می شوند به این تکنیک Data Gram نیز می گویند. TCP/UDP : در پشته پروتکلی TCP/IP دونوع ارتباط می توان با کامپیوتر دور ایجاد کرد: - اتصال به کامپیوتر راه دور به وسیله سوکت Data Stream. - اتصال به کامپیوتر راه دور به وسیله سوکت Data Gram. به طورخلاصه به نوع ارتباط اول (اتصال گرا) ارتباط از نوع TCP گفته می شود. واگر نوع برقراری ارتباط به حالت (غیر اتصال گرا ) باشد به آن UDP گفته می شود. دوستان بهتره هست قبلا حتما مدل مرجع OSI برای شناخت بهتر لایه های شبکه را مطالعه نمایید. به علت مباحث تکنیکی و کمی هم پیچیده مدل OSI ما در این آموزش وارد جزئیات نخواهیم شد. تحقیق درباره آن را به خودتون واگذار میکنم. معرفی توابع مهم مورد استفاده در برنامه نویسی TCPIP با زبان C کتابخانه کار با سوکت های در زبان C می تواند از سیستم عاملی به سیستم عاملی دیگر متفاوت باشد. همینطور از سخت افزاری به سخت افزاری دیگر به عنوان مثال برای برنامه نویسی شبکه در سیستم عامل ویندوز ما از هدر فایل winsock2.h استفاده می کنیم که خود نگارشهای مختلفی دارد وبرای برنامه نویسی شبکه در سیستم عامل های خانواده NIX* از هدر فایل sys/socket.h , sys/type.h و هدرهای دیگر استفاده می کنیم که البته می توان با اقدامی هوشمندانه توسط راهنمای کامپایلر نوع سیستم عامل را تشخیص داد وبعد هدر های مربوط به هر سیستم عامل را مورد استفاده قرارداد. البته باید عرض کنم که توابع مورد استفاده در همه انواع هدرها یکسان می باشند، مگر در معدود مواردی که هر کجا احساس نیاز شود ذکر خواهند شد. شرح توابع مهم موجود در هدر فایل winsock2.h int WSAStartup( WORD wVersionRequested , LPWSADATA lpWSAData ); این تابع برای آماده سازی و بار گذاری اولیه سیستم عامل برای اجرای برنامه تحت شبکه می باشد. آرگومان های آن عبارتند از : wVersionRequested شماره نگارش هدر winsock.h است که برای تبدیل این شماره به نوع WORD می توان از ماکرو makeword استفاده کرد. نکته فنی : به هر 4 بیت یک NIBBLE گفته می شود. وبه هر 8 بیت یک BYTE گفته می شود. وبه هر 16 بیت یا دو بایت یک WORD گفته می شود. به عنوان مثال : WORD wVersionRequested=makeword(2,0); winsock1.1.h و برای نگارش WORD wVersionRequested=makeword(2,1); و آرگومان دوم یک متغیر از نوع ساختمان داده ای WSADATA می باشد که در هدر winsock تعریف شده است. اعضای این ساختمان و نحوه تعریف آن به این صورت است: typedef struct WSAData{ WORD wVersion; WORD wHighVersion; char szDescription[WSADESCRIPTION_LEN+1]; char szSystemStatus[WSASYS_STATUS_LEN+1]; unsigned short iMaxSockets; unsigned short iMaxUdpDg; char FAR* lpVendorInfo; } WSADATA , *LPWSADATA; مثال کاربرد : WSADATA wsaData; WORD wVersionRequested=makeword(2,0); WSAStatrtup(wVersionRequested,wsaData); این تابع در صورت موفقیت مقدار صفر را بر می گرداند و در صورت شکست کد خطای رخ داده را بر می گرداند. int WSACleanup(void); از این تابع برای اتمام بارگذاری مربوط به شکست سیستم عامل استفاده می شود. این تابع در صورت موفقیت مقدار صفر و در صورت بروز خطا ثابتی برابر با SOCKET_ERROR را برمی گرداند. تابع socket : SOCKET socket( int af, int type, int protocol ); از تابع فوق برای تعریف وایجاد یک پورت استفاده می کنیم .و مقدار برگشتی آن توصیف کننده پورت مورد نظر است آرگومانهای تابع به ترتیب از این قرار هستند: af - مشخص کننده نوع آدرس است که برای کاربری اینترنت برابر ثابت AF_INET است برای کنترل سوکت ها در یونیکس برابر ثابت AF_UNIX است و مقادیر دیگر... type - مشخص کننده نوع ارتباطها می باشد که برای ارتباط TCP ثابت SOCK_STREAM و برای یک ارتباط UDP برابر SOCK_DGRAM است. protocol - هم نماینگر نوع پروتکل انتخابی ماست برای یک ارتباط TCP برابر با ثابت IPPROTO_TCP یا مقدار عددی 0 وبرای UDP مقدار عددی 1 یا ثابت IPPROTO_UDP می باشد. این تابع در صورت موفقیت مقدار برگشتی اش توصیفات مربوط به سوکت مورد نظر ماست و در صورت بروز خطا ثابتی به نام INVALID_SOCKET را بر می گرداند. ادامه دارد...
  22. 1 امتیاز
    مراحل ساخت برنامه‌ در زبان سی‌پلاس‌پلاس پیش نویس ۰.۶ قبل از هر چیز به اینفوگرافی زیر توجه کنید که مراحل ساخت برنامه در سی‌پلاس‌پلاس را نشان می‌دهد. مقدمه‌ای بر همگردانی (کامپایل) و اتصال (لینک کردن) این سند مرور مختصری در رابطه با مراحل را برای شما فراهم می‌کند تا به شما در درک دستورات مختلف برای تبدیل و اجرای برنامه‌ی خودتان کمک کند. تبدیل مجموعه‌ای از فایل‌های منبع و هدر در سی‌پلاس‌پلاس به یک فایل خروجی و اجرایی در چندین گام (به طور معمول در چهار گام) پیش‌پردازنده (Preprocessors)، کامپایل و گرد‌آوری (Compilation)، اسمبلر (Assmbler) و پیوند دهنده (Linker) تقسیم می‌شود. قبل از هر چیز اگر در محیط توسعه‌ی Qt Creator داخل فایل .pro مقدار زیر را وارد کنید، تا بتوانید فایل‌های ساخته شده‌ی موقت در زمان کامپایل را مشاهده کنید. QMAKE_CXXFLAGS += -save-temps این دستور اجازه‌ی آن را خواهد داد تا فایل‌هایی با پسوند .ii و .s در شاخه‌ی بیلد پروژه تولید شوند که در ادامه به آن‌ها اشاره شده است. تعریف پیش‌پردازنده پیش‌پردازنده‌ها (Preprocessors) درواقع دستوراتی هستند که اجازه می‌دهند تا کامپایلر قبل از آغاز کردن مراحل کامپایل دستوراتی را دریافت کند. پیش‌پردازنده‌ها توسط هشتگ (#) مشخص می‌شوند این نماد در سی‌++ مشخص میکند که دستور فوق از نوع پیش‌پردازنده می‌باشد که نتیجه‌ی آن در قالب ماکرو (Macro) در دسترس خواهد بود. برای مثال ماکروی __DATE__ توسط پیش‌پردازنده‌ها از قبل تعریف شده است که مقدار تاریخ و زمان را بازگشت می‌دهد. بنابراین هرکجا که از آن استفاده شود کامپایلر آن را جایگزین متن خواهد کرد. در شکل زیر مرحله‌ای که از پیش‌پردازنده‌ها استفاده می‌شود آمده است: پیش‌پردازنده، کامپایل (گردآوری کردن)، لینک (پیوند کردن) و ساخت برنامه اجرایی فرایند تبدیل مجموعه‌ای از فایل‌های متنی هِدر و سورس سی‌++ را «ساخت» یا همان Building می‌گویند. از آنجایی که ممکن است کُد پروژه در بسیاری از فایل‌ها هدر و سورس سی++ توسعه و گسترش یابدمراحل ساخت در چند گام کوچک صورت می‌گیرد. یکی از رایج‌ترین موارد در مراحل گردآوری (ترجمه‌ی یک کد سی‌پلاس‌پلاس به دستورالعمل‌های قابل فهم ماشین) است. اما گام‌های دیگری نیز وجود دارد، پیش‌پردازنده و لینک (پیوند‌ها) این بخش به طور خلاصه توضیح می‌دهد که چه اتفاقی در هر یک از مراحل رُخ می‌دهد. یک کامپایلر یک برنامه‌ی خاص است که پردازش اظهارات (دستورات) نوشته شده در یک زبان برنامه‌نویسی خاص را به یک زبان ماشین که قابل فهم برای پردازنده می‌باشد تبدیل کند. به طور معمول یک برنامه‌نویس با استفاده از یک ویرایشگر که به محیط توسعه‌ی یکپارچه‌ی نرم‌افزار (IDE) مشهور است توسط زبان برنامه‌نویسی مانند ++C دستورات (اظهارات) را می‌نویسد. فایل ایجاد شده با نام (filename.cpp در زبان برنامه‌نویسی سی‌پلاس‌پلاس) شامل محتوایی است که معمولاً به عنوان دستورات برنامه‌نویسی سطح بالا نامیده می‌شود. سپس برنامه‌نویس کامپایلرِ مناسب برای زبان برنامه‌نویسی مانند سی++ را اجرا می‌کند و نام فایل‌هایی که حاوی دستورات هستند را برای کامپایل مشخص می‌کند که این انتخاب و مشخص سازی توسط IDE به راحتی قابل مدیریت است. پس از آن، کار کامپایلر این است که فایل‌های منبع .cpp را جمع آوری کرده و پیش‌پردازنده‌ها را بررسی کند تا دستورات احتمالی را اجرا نماید که نتیجه‌ی این مرحله در فایلی با پسوند .ii ر قالب filename.ii تولید می‌شود که در این فرایند نیز خط به خط کُد‌های موجود در آن‌ها را بررسی می‌کند تا خطاهای احتمالی نحو (سینتکس - Syntax) بررسی می‌شود و آن‌ها را به طور ترتیبی به دستورالعمل‌های سطح ماشین تبدیل کند. توجه داشته باشید که هر نوع پردازنده‌ی کامپیوتر دارای مجموعه‌ای از دستورالعمل‌هایِ ماشین خودش است. بنابراین کامپایلر تنها برای سی++ نیست، بلکه برای اهداف و مقاصد خاص هر پلتفرم است. پس کد‌هایی که توسط پیش‌پردازنده سی‌پلاس‌پلاس به زبان اسمبلی برای معماری مورد نظر در پلتفرم مقصدترجمه شده‌اند نتایج آن در فایلی با پسوند .ss در قالب filename.ss قابل نمایش هستند که در حالت عادی قابل رویت نیست. توجه داشته باشید که باید در این مرحله باید مشخص شود برنامه قرار است توسط چه نوع پردازنده‌ای تحتِ چه نوع معماری مونتاژ (اسمبل) شود. برای مثال پردازنده‌ها با انواع معماری‌های مختلف وجود دارند که برخی از آن‌ها به صورت x86-x64، x64، ARMv7، aarch64 غیره ... می‌باشند. شکل یک (کامپایل یک فایل منبع ++C) مرحله‌ی سوم را در نظر داشته باشید که عمل کامپایل فایل سی‌پلاس‌پلاس در دو مرحله قبلی یک فایل اجرایی را تولید نمی‌کند. برنامه‌ای که توصیف شده است، احتمالاً توابعی را در رابط‌های برنامه‌نویسی (API) و یا توابع ریاضی یا توابع مرتبط با I/O را فراخوانی کند که ممکن است شامل فایل‌های هدر مانند iostream یا fstream و حتی ماژول‌های دیگری که در زبان‌ تعبیه شده‌اند را داشته باشد که فایل تولید شده توسط کامپایلر در این مرحله یک فایل شیء نامیده می‌شود که با پسوند .o به صورت filename.o تولید خواهد شد که علاوه بر دستورالعمل‌های تبدیل شده به کد ماشین، شامل توابع و دستورالعمل‌های خارجی نیز می‌باشد. هرچند در این مرحله دستورات تبدیل به دستورالعمل‌های قابل فهم توسط پردازنده شده‌اند اما فعلاً قابل اجرا نیستند چرا که باید این توابع خارجی افزوده شده را به آن لینک کرد که در مرحله‌ی بعد یعنی مرحله‌ی چهارم اتفاق می‌افتد. در نهایت مرحله‌ی چهارم فایل با پسوند .o که شامل کد‌های تولید شده توسط کامپایلر به زبان ماشین است که پردازنده‌ها می‌توانند این دستورات را درک کنند که همراه با کد‌های تولید شده‌ی هر کتابخانه‌ی دیگری که مورد نیاز است توسط لینکر (لینک شده) و در نهایت جهت تولید یک فایل اجرایی مورد استفاده قرار می‌گیرند که نوع آن فایل از نوع اجرایی یا در واقع Executable File خواهد بود. شرح کامل فرایند ساخت فایل اجرایی اکثر پروژه‌ها دارای مجموعه‌ای از فایل‌های هدر سی++ هستند، که امکان ماژولار شدن در آن را فراهم می‌کند و مجموعه‌ای از آن می‌تواند به عنوان بخش‌های کوچکی از برنامه محسوب شوند. برای ساخت چنین پروژه‌هایی هر فایل سی‌پلاس‌پلاس باید کامپایل شود و سپس فایل‌های ساخته شده در قالب شیء (آبجکت) باید همراه توابع و کتابخانه‌های دیگر لینک (پیوند) شوند. البته هر گام از مراحل کامپایل شامل یک مرحله پیش‌پردازنده است که دستورالعمل # عمل تغییرات و اصلاحیه‌ها را در فایل متن اعمال می‌کند. شکل زیر فرایند ساخت چند فایل به صورت همزمان را نشان می‌دهد:
  23. 1 امتیاز
    در این قسمت قصد داریم تا با چند مفهوم پایه‌ای تر در سیستم عامل ویندوز آشنا شویم. در ابتدا مفهوم (VAS (Virtual address spaces را مورد بررسی قرار می‌دهیم. فضای آدرس‌های مجازی (VAS) یک مُدل برای مدیریت بهتر حافظه می‌باشد، زمانی که یک پردازنده اقدام به خواندن یا نوشنتن در یک مکان حافظه می‌کند، از یک آدرس مجازی استفاده می‌کند. به عنوان بخشی از عملیات خواندن یا نوشتن، پردازنده آدرس مجازی را به آدرس فیزیکی ترجمه می‌کند. دسترسی به حافظه از طریق یک آدرس مجازی مزایای زیر را دارد : یک برنامه می‌تواند از محدوده‌ی مجاور آدرس های مجازی برای دسترسی به یک بافر حافظه بزرگ استفاده کند که در حافظه فیزیکی به یکدیگر متصل نیستند. یک برنامه می‌تواند طیفی از آدرس های مجازی برای دسترسی به بافری بزرگتر از حافظه فیزیکی موجود را مورد استفاده قرار دهد. حافظه‌ی فیزیکی ( که به اندازه 4 کیلوبایت می‌باشد) را به فایل دیسک می‌فرستد. صفحات داده یا کد بین حافظه‌ی فیزیکی و دیسک در صورت مورد نیاز منتقل می‌گردد (اولویت با صفحات قدیمی تر می‌باشد.که دیرتر به آنها مراجعه شده است). آدرس های مجازی مورد استفاده در فرایندهای مختلف از یکدیگر جدا شده اند. کد در یک فرآیند نمی‌تواند حافظه‌ی فیزیکی را که توسط فرآیند دیگری یا سیستم عامل مورد استفاده قرار می‌گیرد تغییر دهد. محدوده‌ی آدرس‌های مجازی که در فرآیند در دسترس است، فضای آدرس مجازی برای فرایند می‌باشد. هر فرایند حالت کاربر، دارای فضای آدرس مجازی خصوصی خود است. برای یک فرایند 32 بیتی، فضای آدرس مجازی معمولاً محدوده‌ی 2 گیگابایتی از 0x00000000 تا 0x7FFFFFFF است. برای یک فرآیند 64 بیتی فضای آدرس مجازی محدوده 8 ترابایتی 0x00000000000 تا 0x7FFFFFFFFFF است. طیفی از آدرس‌های مجازی گاهی اوقات طیفی از حافظه‌های مجازی نامیده می‌شوند. این نمودار برخی از ویژگی های آدرس مجازی را نشان می‌دهد: این نمودار فضاهای آدرس مجازی را برای دو فرایند 64 بیتی نشان می‌دهد: Notepad.exe و MyApp.exe هر فرایند دارای فضای آدرس مجازی خود است که از 0x000'0000000 تا 0x7FF'FFFFFFFF قرار دارد. هر بلوک آبی نشان دهنده یک صفحه (به اندازه 4 کیلوبایت) از حافظه مجازی یا فیزیکی است. توجه داشته باشید که فرایند Notepad از سه صفحه پیوندی از آدرس های مجازی استفاده می‌کند، با شروع از آدرس 0x7F7'93950000. اما این سه صفحه مجاور آدرس‌های مجازی به صفحات غیر مستقیم در حافظه فیزیکی نقش می‌شود. همچنین توجه کنید که هر دو فرایند با استفاده از یک صفحه از حافظه مجازی شروع از 0x7F7'93950000 استفاده می‌کنند، اما آن صفحات مجازی به صفحات مختلف حافظه فیزیکی نقش می‌شوند. فضای کاربر و فضای سیستم: فرایند‌هایی مانند Notepad.exe و MyApp.exe در حالت کاربر اجرا می‌شوند. اجزاء اصلی سیستم‌عامل و بسیاری از درایور‌ها بیشتر در حالت کِرنل مورد استفاده قرار می‌گیرند. هر فرایندِ حالت کاربر دارای فضای آدرس خود است ، اما تمام کد‌هایی که در حالت هسته اجرا می‌گردند، یک فضای آدرس مجزا به نام فضای سیستم دارند (دارای فضای آدرس مشترک هستند) . فضای آدرس مجازی برای فرآیند کاربر حالت فعلی کاربر، user space نامیده می‌شود. در ویندوز 32 بیت، فضای آدرس مجازی موجود در دسترس 2 به توان 32 بایت (4 گیگابایت) است. معمولاً 2 گیگابایت کمتر برای فضای کاربر می‌باشد و ۲ گیگابایت بالاتر برای فضای سیستر در نظر گرفته می‌شود. در ویندوز 32 بیتی شما میتوانید گزینه ای (در هنگام بوت شدن) را مشخص کنید که بیش از 2 گیگابایت برای فضای کاربر در دسترس باشد . در نتیجه آدر مجازی کمتری در دسترس سیستم قرار می‌گیرد. شما می‌توانید حجم فضای کاربر را تا 3 گیگا بایت افزایش دهید ، در این صورت فقط 1 گیگ فضا برای سیستم باقش می‌ماند. در ویندوز های 64 بیتی مقدار فضای آدرس مجازی 2 به توان 64 بایت (16 اگزابایت) می‌باشد . اما تنها بخش کوچکی از این محدوده استفاده میگردد محدوده 8 ترابایت از 0x000'00000000 تا0x7FF'FFFFFFFF برای فضای کاربر استفاده می‌شود و بخش هایی از 248 ترابایت از 0xFFFF0800'00000000 تا 0xFFFFFFFF'FFFFFFFF برای فضای سیستم استفاده می‌گردد. کُدِ در حال اجرا در حالت کاربر دسترسی به فضای کاربر دارد، اما دسترسی به فضایِ سیستم ندارد‌. این محدودیت باعث می‌شود که کد کاربر حالت خواندن یا تغییر ساختار داده های محافظت شده سیستم عامل را نداشته باشد. کد در حالِ اجرا در حالت هسته دارای دسترسی به فضای کاربر و فضای سیستم می‌باشد. درایور‌ها (راه‌انداز‌هایی) که در حالت هسته اجرا می‌شوند باید در نوشتن یا خواندن در فضای آدرس کاربر به دلایل زیر بسیار محتاط باشد: یک برنامه یوزر مُد (حالت کاربر)، یک در خواست برای خواندن برخی از داده‌ها را به یک دستگاه می‌فرستد. این برنامه آدرس اولیه یک بافر برای دریافت داده ها را فراهم می‌کند. یک دستگاه روتینِ درایور در حالت اجرا در حالت هسته‌، عملیات خواندن را شروع می‌کند و کنترل را به تماس گیرنده خود بازمی‌گرداند سپس اینتراپت دستگاه هر نخ (ترد) را که در حال اجرا است را قطع می‌کند. در این مرحله، درایور نباید دادهها را به آدرس اولی‌ها ارسال کند که در برنامه کاربر، در قسمت اول به آن اشاره شد. این آدرس در فضای آدرس مجازی فرایند است که درخواست را آغاز کرده است، که به احتمال زیاد همانند فرایند فعلی نیست. مخازن (استخر حافظه) صفحه‌بندی شده و صفحه‌بندی نشده (Paged pool and Nonpaged pool) در فضای کاربری، تمام صفحات حافظه فیزیکی را می‌توان به عنوان یک فایل دیسک به صورت صحیح برگرداند‌. در فضای سیستم، برخی صفحات فیزیکی می‌توانند از بیین بروند و برخی نیز نمی‌توانند. فضای سیستم دارای دو منطقه برای تخصیص حافظه پویا می‌باشد : paged pool و nonpaged pool. در حالت paged pool حافظه می‌تواند به صورت فایل (در صورت نیاز) به دیسک منتقل گردد. در حالت nonpaged pool هرگز حافظه نمی‌تواند به دیسک منتقل گردد. کتابخانه‌ی پیوند پویا و فایل‌های اجرایی (DLL و PE) در این قسمت کمی با ساختار فایل های اجرایی و بخصوص DLL ها آشنا می‌شویم. در خلاصه ترین حالت می‌توان گفت Dynamic link library نام کتابخانه‌هایی است که توسط برنامه ها استفاده می‌شوند و توسط مایکروسافت پیاده سازی شده ( که اغلب دارای پسوند dll می‌باشند). این فایل ها همانند ساختار فایل های exe در ویندوز دارای ساختار (PE (Portable Executable می‌باشد. این کتابخانه‌ها می‌توانند شامل کد و داده و منابع (ریسورس‌ها) باشند. یکی از مزایای فایل های dll این است که یک بار در حافظه بارگذاری می‌شود و می‌تواند توسط چندین برنامه مورد استفاده قرار گیرد (به صورت مجازی برای هر برنامه کپی می‌شود). می‌توان dll ها را در موقه نیاز در برنامه بارگذاری کرد و هر جا که دیگر مورد نیاز نبود آن را خالی (Unload) کرد. از طرفی دیگر می‌توان از آن برای استفاده از برنامه های قابل به‌روز‌رسانی نیز استفاده کرد به این صورت که می‌توان آیکن‌ها ، فونتها و کدهایی که در هسته اصلی برنامه جایگاهی ندارند را درون dll ها قرار داد و در هنگام به‌روز‌رسانی تنها این dll ها را تعویض کرد. هر فایل اجرایی جدا از کدها و دادههای خود می‌تواند اطلاعات را از خارج از خود و از dll بگیرد. در هر فایل pe بخشی از هدر فایل، شامل آدرس جدول آدرس وارد کردن (Import) می‌شود که اطلاعات موجود در آن، آدرس توابعی که از dll ها فراخوانی می‌شود را در خود نگه داری می‌کنند ( البته این آدرس ها با پایه‌ی آدرس dll ترکیب می‌شوند که در تصویر دوم هم قابل مشاهده است). تصویر زیر خلاصه‌ای ار هدر فایلهایی با ساختار PE می‌باشد. در این عکس import adress table حاوی آدرس iat در برنامه می‌باشد. شکل زیر یک توضیح کلی تر و بهتر در اختیار ما می‌گذارد. در این شکل می‌توان iat را بین دو بخش کد و داده ببینید. نمونه ای از فراخوانی یکی از توابع dll ها را در زیر می‌توان مشاهده کرد. این تصویر کد اسمبلی یک برنامه می‌باشد. مثال های کاربردی: مثال اول: در مثال اول یک dll ساخته و آن را با rundll32.exe اجرا کنید. از قسمت پروژه‌ جدید (New project) در قسمت سی‌پلاس‌پلاس پروژه ای از نوع win32project بسازید. در صفحه باز شده Next را بزنید. dll را انتخاب کرده و سپس تیک Empty project را بزنید و پروژه را بسازید. سپس به پروژه خود یک فایل cpp اضافه کنید و کد‌های زیر را در آن بنویسید: //[dll01.dll] #include <windows.h> extern "C" __declspec (dllexport) void __cdecl hello() { ::MessageBox(0, L"hello world", 0, 0); } BOOL APIENTRY Dll(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) { return TRUE; } برنامه را کامپایل کنید، سپس cmd را باز کرده و دستور زیر را در آن تایپ کنید . (در آدرسی که dll قرار دارد دستور را اجرا کنید یا این که آدرس کامل dll را به آن بدهید) c:\rundll32.exe dll01.dll,hello شکل کلی استفاده از این دستور: RUNDLL32.EXE <dllname>,<entrypoint> <optional arguments> با اجرای این دستور پنجره‌ای را می‌بینید که در آن پیغام موجود در dll را چاپ می‌کند. مثال دوم: در این مثال‌، دی‌ال‌ال (dll) ای که در مثال قبل ساختید را در یک برنامه دیگر با زبان c++ ایجاد و بعد از اتمام کار، آن را آن بارگذاری کنید. کُد برنامه مورد نظر به صورت زیر خواهد بود: #include <windows.h> #include <stdio.h> typedef void(__cdecl *MYPROC)(); int main(void) { HINSTANCE hinstLib; MYPROC ProcAdd; BOOL fFreeResult, fRunTimeLinkSuccess = FALSE; // Get a handle to the DLL module. hinstLib = LoadLibrary(TEXT("c://dll01.dll")); // If the handle is valid, try to get the function address. if (hinstLib != NULL) { ProcAdd = (MYPROC)GetProcAddress(hinstLib, "hello"); // If the function address is valid, call the function. if (NULL != ProcAdd) { printf("dll is loaded\n"); fRunTimeLinkSuccess = TRUE; (ProcAdd)(); } // Free the DLL module. fFreeResult = FreeLibrary(hinstLib); if (fFreeResult && ProcAdd) { printf("dll is Unloaded\n"); } else if (!fFreeResult && ProcAdd) { printf("error , dll is not Unload\n"); } } // If unable to call the DLL function, use an alternative. if (!fRunTimeLinkSuccess) { printf("Message printed from executable\n"); } system("pause"); return 0; }
  24. 1 امتیاز
    جزئیات در ++C ویرایش ۱۷: کد نویسی ساده با توجه به مشخصاتی که در سی‌پلاس‌پلاس ۱۷ که ارائه شده است، ویژگی‌های جدید برای این معرفی می‌شوند تا کد شما تمیز تر و بهتر اعمال شوند. این مقاله را برای مطلع شدن از جزئیات بیشتر بخوانید. با هر استاندارد سی‌‌‌پلاس‌‌پلاس که ارائه می‌شود، هدف از آن تولید کد ساده تر، واضح تر و رسا تر می‌باشد. سی‌پلاس‌پلاس ۱۷ چندین ویژگی بزرگ زبان را ارائه می‌دهد که باعث می‌شود کد ما زیباتر و بهتر شود. بنابراین بیایید باهم یک نگاهی به این ویژگی‌ها داشته باشیم. ممکن است شما بگویید که بیشترین ویژگی‌های جدید زبان (به جز پیشرفت های کتابخانه استاندارد - STL) برای نوشتن کد ساده تر و پاکتر می‌باشند. با توجه به مجموعه جزئیات سی‌پلاس‌پلاس ۱۷ که بسیاری از چیزهای بزرگ را مورد بررسی قرار داده است، ما امروز تنها برای بعضی از ویژگی ها که از داخل این مجموعه عظیم بیرون کشیده‌ایم اشاره خواهیم داشت که باعث می‌شود کد شما فشرده‌تر و بهینه تر شود. پیوند‌های ساخت یافته / اعلان‌های تجزیه عبارت Init-statement برای if/switch متغیرهای درون خطی (inline) شرط constexpr if و چند موارد دیگر پیوند‌های ساخت یافته، آیا اغلب با tuple ها کار کرده‌اید؟ اگر نه، پس احتمالاً باید به آن نگاهی کنید. tuple ها تنها برای بازگشت مقادیر چند گانه از یک تابع پیشنهاد نمی‌شوند، آنها پشتیبانی ویژگی‌ای از زبان را داشتند. به طوری که باعث می‌شود کد ساده تر و پاکتر شود. برای مثال (std::tie که از مرجع اصلی سی‌پلاس‌پلاس به دست آمده است) به صورت زیر است: std::set<S> mySet; S value{42, "Test", 3.14}; std::set<S>::iterator iter; bool inserted; // unpacks the return val of insert into iter and inserted std::tie(iter, inserted) = mySet.insert(value); if (inserted) std::cout << "Value was inserted\n"; توجه داشته باشید که باید iter و inserted را ابتدا وارد کرده باشید. سپس شما می‌‌توانید از std::tie استفاده کنید. با این حال این بخش کوچکی از کد نمونه است: std::set<S> mySet; S value{42, "Test", 3.14}; auto [iter, inserted] = mySet.insert(value); اینجا توجه داشته باشید که یک خط به جای سه خط جایگزین شده است! این کد ساده‌تر و خوانا‌تر و درعین حال ایمن‌ تر است، اینطور نیست؟ همچنین، شما هم اکنون می‌‌توانید از const استفاده کنید و آن را به صورت const auto [iter inserted] بنویسید که صحیح است. پیوند ساختاری تنها به tuple ها ختم نمی‌شود، چرا که ما سه مورد دیگر را داریم: اگر مقدار دهی اولیه یک آرایه باشد: // works with arrays: double myArray[3] = { 1.0, 2.0, 3.0 }; auto [a, b, c] = myArray; اگر مقدار دهی اولیه std::tuple_size<> را پشتیبانی کند و تابع get را فراهم کند که شایع ترین مورد است. auto [a, b] = myPair; // binds myPair.first/second به عبارت دیگر، شما می‌‌توانید کلاس‌های خود را پشتیبانی کنید، با فرض این که شما تابع get را در پیاده سازی رابط کلاس خود اضافه کرده باشید. اگر مقدار دهی اولیه فقط شامل اعضای عمومی شود در این صورت: struct S { int x1 : 2; volatile double y1; }; S f(); const auto [ x, y ] = f(); در حال حاضر این روش برای دریافت یک مرجع از یک عضو tuple آسان است. auto& [ refA, refB, refC, refD ] = myTuple; و یکی از جالب‌ترین استفاده‌‌ها (پشتیبانی از حلقه‌ها است): std::map myMap; for (const auto & [k,v] : myMap) { // k - key // v - value } پیوند ساختاری یا تقسیم بندی اعلان‌ها برای این ویژگی، ممکن است شما نام های دیگری را دیده باشید، "اعلان تجزیه". همانطور که می‌‌بینیم، این دو نام در نظر گرفته شده است، اما فعلاً استاندارد سازی در حالت پیش‌نویسه است و با نام "پیوند‌های ساختاری" می‌‌باشند. جزئیات بیشتر در رابطه با این مورد در اسناد P0217R3، P0144R0 و P0615R0 موجود هستند. همچنین این مورد با کامپایلر‌‌های GCC 7.0،MSVC2017 و Clang 4.0 سازگار است. عبارت Init-statement برای if/switch نسخه جدید عبارت شرطی if و switch در سی‌پلاس‌پلاس جدید به صورت زیر است: if (init; condition) , switch (init; condition) قبلاً باید به صورت زیر می‌نوشتیم: { auto val = GetValue(); if (condition(val)) // on success else // on false... } در اینجا val یک دامنه جدا کننده بدون دارد که باعث "نشت - فقدان" در خاتمه دهنده دامنه خواهد شد. در حالی که شما در نسخه جدید می‌‌‌توانید به صورت زیر بنویسید: if (auto val = GetValue(); condition(val)) // on success else // on false... متغیر val تنها در داخل عبارات if و else قابل رویت است، بنابراین آن یک "نَشت" نخواهد داشت. condition ممکن است چندین عبارت باشد نه تنها if بنابراین متغیر val یکی از دو مقدار true/false را خواهد داشت. چرا این ویژگی کاربرد دارد؟ اجازه دهید تا به شما بگوییم زمانی که می‌خواهید چندین چیز را در یک رشته را جستجو کنید به صورت زیر خواهید داشت: const std::string myString = "My Hello World Wow"; const auto it = myString.find("Hello"); if (it != std::string::npos) std::cout << it << " Hello\n" const auto it2 = myString.find("World"); if (it2 != std::string::npos) std::cout << it2 << " World\n" ما باید از نام‌‌های مختلفی برای it استفاده کنیم و یا اینکه آن را با دامنه خاتمه دهنده جدا سازیم. { const auto it = myString.find("Hello"); if (it != std::string::npos) std::cout << it << " Hello\n" } { const auto it = myString.find("World"); if (it != std::string::npos) std::cout << it << " World\n" } عبارت جدید شرطی if در نسخه جدید یک دامنه اضافه را در یک خط ایجاد می‌‌کند. if (const auto it = myString.find("Hello"); it != std::string::npos) std::cout << it << " Hello\n"; if (const auto it = myString.find("World"); it != std::string::npos) std::cout << it << " World\n"; همانطور که قبلاً اشاره شده است، متغیر تعریف شده در عبارت شرطی if در بلوک دیگری قابل مشاهده است. بنابراین شما می‌‌تواین به صورت زیر بنویسید: if (const auto it = myString.find("World"); it != std::string::npos) std::cout << it << " World\n"; else std::cout << it << " not found!!\n"; به علاوه، شما می‌‌توانید آن را با پیوند ساختاری بر اساس کد نمونه از جانب (Herb Sutter) استفاده کنید: // better together: structured bindings + if initializer if (auto [iter, succeeded] = mymap.insert(value); succeeded) { use(iter); // ok // ... } // iter and succeeded are destroyed here جزئیات بیشتر در اسناد زیر آمده است: سند P0305R1 ویدیو موجود در یوتیوب با عنوان (C++ Weekly - Ep 21 C++17’s if and switch Init Statements) این مورد با کامپایلر‌های GCC 7.0،MSVC-2017.3 و Clang 3.9 سازگار است. متغیرهای درون خطی (inline) با شروع مقدار دهی داده‌های غیر استاتیک، اکنون می‌‌توانیم یک متغیر عضو را دی یک مکان اعلام کنیم. با این حال، با متغیر‌های استاتیک یا const static، معمولاً باید آن را در برخی از فایل‌های cpp تعریف کنید. در سی‌پلاس‌پلاس ۱۱ و کلید واژه constexpr که مارا قادر می‌سازد تا در یک مکان اعلان و تعریف متغیر‌های استاتیک را انجام دهیم، اما این امکان تنها به constexpr محدود می‌‌شود. قبلاً فقط روشها/توابع می‌‌توانستند به عنوان inline تعریف شوند، حالا شما می‌‌توانید این کار را با متغیر ها در داخل فایل هدر انجام دهید. یک متغیر اعلام شده درون خطی معنای مشاهبی دارد بنابراین همانند یک تابع inline تعریف می‌‌شود. آن را می‌‌توان به واحد‌های ترجمه چند گانه نیز تعریف کرد. مثال‌‌های زیر را ببینید: struct MyClass { static const int sValue; }; inline int const MyClass::sValue = 777; و حتی struct MyClass { inline static const int sValue = 777; }; همچنین توجه داشته باشید که متغیر‌های constexpr به طور ضمنی inline هستند، بنابراین نیازی به استفاده به صورت constepr inline myVar = 10; نمی‌‌باشد. این ویژگی چرا کد را ساده تر می‌‌کند؟ برای مثال، تعداد زیادی از هدرها در کتابخانه تنها تعدادی از روش های (هک‌) را محدود می‌‌کنند (مانند استفاده توابع درون خطیinline و یا قالب ها) و در نهایت مزیت constexpr این است که مقدار دهی اولیه شما نباید constexpr باشد. جزئیات بیشتر در رابطه با این ویژگی در سند زیر موجود است: سند P0386R2 این مورد با کامپایلر‌های GCC 7.0 و Clang 3.9 سازگار است اما فعلاً با MSVC سازگاری ندارد. ویژگی مربوط به constexpr if ممکن است در بعضی جاها به قابلیت std::enable_if در سی‌پلاس‌پلاس ۱۴ نگاه کنید که آن به راحتی با constexpr if جایگزین می‌شود. بنابراین، در اکثر موارد، ما اکنون می‌‌توانیم تنها با نوشتن عبارت یک constexpr if این کار را بهتر و تمیز تر انجام دهیم. این ویژگی برای برنامه نویسی metaprogramming/template بسیار مهم است که احتمالاً طبیعت آن بسیار پیچیده خواهد بود. یک مثال ساده با تابع Fibonacci: template<int N> constexpr int fibonacci() { return fibonacci<N-1>() + fibonacci<N-2>(); } template<> constexpr int fibonacci<1>() { return 1; } template<> constexpr int fibonacci<0>() { return 0; } حال می‌‌توان آن را تقریباً در یک حالت نرمال (نسخه بدون کامپایل) نوشت: template<int N> constexpr int fibonacci() { if constexpr (N>=2) return fibonacci<N-1>() + fibonacci<N-2>(); else return N; } در رویداد ۱۸ هم جلسات هفتگی سی‌پلاس‌پلاس در جیسون ترنر نمونه‌‌ای را می‌‌توان یافت که در آن عبارت constexpr if هیچ منطق اتصال کوتاهی را در زمان کامپایل انجام نمی‌‌‌دهد، بنابراین این کد باید کامپایل شود: if constexpr (std::is_integral<T>::value && std::numeric_limits<T>::min() < 10) { } در کد فوق برای T شما در std::strin‌g خطایی کامپایل را دریافت خواهید کرد زیرا numeric_limits برای رشته ها تعریف نشده اند. در جلسات C++NOW 2017 آقای Bryce Leblbach با عنوان جلسه خود C++17 Features در ۱۶ دقیقه مثال بسیار زیبایی را در رابطه باconstexpr if زد که می‌‌تواند برای تابع get استفاده شود که آن برای پیوند ساختاری مورد استفاده قرار می‌‌گیرد. struct S { int n; std::string s; float d; }; template <std::size_t I> auto& get(S& s) { if constexpr (I == 0) return s.n; else if constexpr (I == 1) return s.s; else if constexpr (I == 2) return s.d; } قبلاً ما باید به صورت زیر می‌‌نوشتیم: template <> auto& get<0>(S &s) { return s.n; } template <> auto& get<1>(S &s) { return s.s; } template <> auto& get<2>(S &s) { return s.d; } همانطور که می‌‌بینید، مشکل سوال برانگیز اینجاست که کد در این جا ساده تر است. اگر چه در این مورد فقط از یک ساختار ساده استفاده شده است، با برخی از نمونه های واقعی دنیا، کد نهایی باید بسیار پیچیده تر از این باشد بنابراین باید constexpr if کد تمیز تری نسبت به این مورد باشد. این مورد با کامپایلر‌های GCC 7.0،MSVC-2017.3 و Clang 3.9 سازگار است. ویژگی‌های دیگر ما می‌‌توانیم در رابطه با بسیاری از ویژگی های جدید سی‌پلاس‌پلاس صحبت کنیم اما در این پست ما بیشتر در رابطه با قطعات بزرگتر تمرکز کرده‌‌ایم. با این حال، فقط برای یادآوری، ممکن است بخواهید ویژگی‌‌های زیر را در نظر بگیرید که آنها نیز کد ها را ساده تر می‌کنند: قالب‌ (template) عبارت Fold الگو برای کلاس ها بنابراین برای ذکر ویژگی‌‌های بیشتر در رابطه با نسخه جدید در پستهای آن ها را پوشش خواهیم داد. شک نکنید که، سی‌پلاس‌پلاس ۱۷ پیشرفت واقعی را در برابر کد های جمع و جور و آسان فراهم گرده است. یکی از بهترین چیزها constexpr است که آن به ما اجازه می‌دهد کد template/metaprogramming را به روش کد استاندارد شده بنویسیم. این یک مزیت بسیار بزرگی است. ویژگی دوم: پیوند ساخت یافته (که حتی برای حلقه ها کار می‌‌کند) مانند حسی را القا می‌‌کند که در زبان‌های پویایی مثل Python وجود دارد. همانطور که می‌‌بینید، تمام ویژگی‌های ذکر شده در حال حاضر در Clang، MSVC و GCC قابل اجرا هستند. اگر شما با نسخه های اخیر این کامپایلر ها کار می‌کنید می‌تواین بلافاصله با سی‌++ ۱۷ کار کرده و آن را تجربه کنید.
  25. 1 امتیاز

    نگارش 8.0.0

    7 دریافت

    کامپایلر کلَنگ (Clang) یک مترجم روبه جلو برای زبان‌های برنامه نویسی C و ++C و Objective-C و ++Objective-C می‌باشد که از LLVM بعنوان زیر ساخت روبه عقب استفاده می‌کند. آرمان کلنگ این است که جایگزین کامپایلر جی‌سی‌سی شود. کلنگ بصورت کاملاً متن باز توسعه میابد و توسط کمپانی‌های بزرگی مانند گوگل و اپل پشتیبانی می‌شود.

    رایگان

این صفحه از پرچمداران بر اساس منطقه زمانی تهران/GMT+04:30 می باشد
×