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

تمامی فعالیت ها

این جریان به طور خودکار بروزرسانی می شود     

  1. دیروز
  2. استفاده از قابلیت جدید تعریف شده در دستور 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; و قطعا تصدیق میکنید که استفاده از این نوع تعریف بسیار کاربردی تر و نگهداری کد را به مراتب بالاتری در پی خواهد داشت.
  3. با سلام همانطور که میدانید زبان سی پلاس پلاس در سالهای اخیر تغییرات شگرف و چشمگیری نسبت به سالهای اولیه پیدایش این زبان داشته است ، به طوریکه کسانی که از استاندارد های قدیمی این زبان استفاده میکنند همگی اذعان دارند که تا چند سال آینده در صورتی که از تکنیک های اضافه شده جدید به این زبان استفاده نشود و آموزش مناسبی برای این تکنیک ها دیده نشود به طور قطع به یقین نمی توان ادعا داشت که زبان سی پلاس پلاس را بلد هستیم و قادر به تولید نرم افزارهایی با قابلیت نگهداری بالاتر و مقایس پذیر تر و با قابلیت استفاده مجدد بالاتر نخواهیم بود. در صورتی که هیچ آشنایی با این سطح از تغییرات در زبان سی پلاس پلاس را ندارید، پیشنهاد میکنم مقاله دوست عزیزم جناب اسدازاده را از این لینک قابلیت‌های ممتاز ++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; } این آموزش ادامه خواهد داشت....
  4. kambiz behnia

    با تشکر از پاسخ بطور خلاصه نتیجه گیری خودم از پاسخ های شما را اینجا می آورم لطفا اطلاح بفرمائید. 1- توابع socket - setsocketopt - bind و listen برای استفاده در Threadها مشکلی ندارند و برای استفاده از آنها نیاز به استفاده متدهایی برای Lockکردن نیست (منظور استفاده از خود توابع بوده و فرض بر این است که پارامترها حتما مختلف خواهند بود) 2- برای استفاده از دستور accept باید از متدهای lock کردن منابع استفاده کرد حتی اگر پارامترها مختلف باشند. 3 - استفاده از توابع select - send , recv همانند قسمت 1 هستند. 4 - اگر یک socket که بطور صحیح ایجاد و قابل استفاده است در اختیار داشته باشیم. میتوانیم در یک Thread فقط کار خواندن و در یک Thread دیگر فقط کار نوشتن روی آنرا انجام دهیم. 5- استفاده از تابع close شرایط واضح خود را دارد و حین و بعد از استفاده از آن پارامتر مشخص شده برای آن نباید در Thread دیگری مورد استفاده باشد. 6 - تابع connect عملکردی مشابه قسمت 5 دارد. سوال 1: اگر مورد 2 صحیح باشد متوجه علت آن نمیشوم چو اگر بقیه برای استفاده در Thread مشکلی نداشته باشند این هم نباید داشته باشد ؟ چون بطور کلی در Thread ها مشکل استفاده از منابه مشترک است آیا توابع ذکرشده منبع حساب میشوند. سوال 2: در بخش اول نپرسیدم نحوه استفاده از errno برای برسی خطا در سوکتها به چه شکل است هر Thread مقدار جداگانه ای برای خود دارد (بعید بنظر میرسد).
  5. هفته گذشته
  6. فرهاد شیری

    بله می توانید در سه نخ جداگانه اجرا کنید! چون شما در حقیقت سه تا سوکت ایجاد میکنید که به یک آدرس متصل میشود پس می توانید در نخ های مختلف این عملیات را انجام دهید.! بله می توانید درنخ های مختلف اجرا کنید! منتهی حتما باید از یک قفل جهت کنترل accept استفاده کنید تا race condition رخ ندهد. بله می توانید از AcceptSock1 در دونخ مجزا استفاده کنید به شرط اینکه تمامی شرایط داده ناحیه اشتراکی را در نخ ها لحاظ کنید. در صورتی که ناحیه اشتراکی به درستی تعریف نشده باشد قطعا Race Condition و بعد هم Dead Lock رخ خواهد داد. بله میتوانید اگر شرایط ذکر کرده در بالا را رعایت کنید. بله امکان خواندن از یک سوکت هم در دونخ مجزا هم وجود دارد ولی قطعا پیچیدگی های زیادی برای داده های اشتراکی خواهید داشت. بله ولی قطعا بدون استفاده از قفل گذاری مناسب و ناحیه اشتراکی خیر! در پایان باید عرض کنم که استفاده از نخ ها در سیستم عامل ویندوز با استفاده از کلاس Thread می باشد. که البته کتابخانه های زیادی برای Concurrency , Parallel Prog وجود دارد ولی تقریبا نحوه استفاده از قفلها در ویندوز و لینوکس مشابه هستند. و در لینوکس با استفاده از کلاس pthread می توانید از نخ ها استفاده کنید. البته توجه داشته باشید که ساختمان نخ ها در هر دو سیستم عامل خیلی باهم متفاوت هستند هرچند برنامه نویس خیلی درگیر این پیچیدگی ها نیست چون خود سیستم عامل مدیریت میکنه. بنابراین برای استفاده از سوکت ها در نخ ها ویا حوضچه های نخ حتما باید قوانین نواحی اشتراکی را مد نظر قرار دهید تا هم از بازدهی هرچه بهتر پردازشگر استفاده کنید وهم برنامه ای به مراتب سریعتر از برنامه های ترتیبی ایجاد کرده باشید. ولی فکر کنم ذکر یک نکته خالی از لطف نباشد: تنها راه ایمن ماندن از خطاها و گرفتاری های نخ ها (استفاده نکردن از نخ هاست.) یعنی اگر در استفاده از نخ ها دچار افراط شوید قطعا برنامه هایی به مراتب کندتر از نسخه های ترتیبی خواهید ساخت.
  7. فرهاد شیری

    درسته که تعداد فایلهای کتابخانه دات نت خیلی زیاد هست و وابستگی ایجاد میکنه ولی توسعه یک نرم افزار تجاری (البته کشور خودمون را عرض میکنم) توسط یک شرکت نرم افزاری معتبر قطعا با دات نت ویا تکنولوژی های جاوا صورت میگیره! و در صورتی هم که حوزه نرم افزار مربوط به مسائل کلان کشور باشه قطعا لینوکس و زبان ++C,C اولین گزینه انتخاب برای توسعه خواهد بود. به همین علت به نظرم بهتره که هیجان زده نشید و نسبت به مایکروسافت یه جوری نشید
  8. Nader

    با سلام، ما به یک برنامه‌نویس مسلط به Qt و QML نیازمندیم. فرایند اجرای پروژه به صورت قراردادی است. محل کار: تهران، جمهوری تلفن تماس: ۰۹۱۱۳۴۳۷۷۹۰
  9. Seyyed Ali Mohammadiye

    موفق باشید. Linux Professional Institute
  10. MahdiGameMaker

    دمت گرم! فقط من قبلا با لینوکس اشنا بودم! ولی بازم بابت دوره lpic ممنونم این اولین تالار گفت و گویی هست که میبینم صورت سئوالم رو تغییر میتونن بدن
  11. الهه انصاری

    اصول طراحی: تضاد

    تا کنون در مجموعه‌ی مقالات اصول طراحی، در مورد تعادل و رنگ صحبت کردیم. در این بخش این روند را با موضوع تضاد (Contrast) ادامه می‌دهیم. تضاد هنگامی رخ می‌دهد که دو عنصر در یک صفحه متفاوت باشند. به عنوان مثال، تضاد می‌تواند خودش را در رنگ‌های متنوع بین متن و رنگ پس زمینه نشان دهد. یا یک عنوان بزرگ و درشت در کنار یک فونت sans-serif برای متن بدنه باشد. یا میتواند تفاوت بین یک گرافیک بزرگ و یک گرافیک کوچک باشد و یا ترکیب یک بافت خشن با بافت ظریف که یک تضاد قابل توجهی ایجاد می‌کند. چشمان ما علاقه‌مند به دیدن تضاد هستند. نکته‌ی حائز اهمیتی که در رابطه با تضاد وجود دارد این است که تضاد باید کاملا چشم‌گیر و غیر جزئی باشد. ۱. تضاد از نظر چشمان ما جذاب است. یکی از دلایل استفاده‌ی ما از تضاد، چه در چاپ و چه در وب، جلب کردن توجه مخاطب است. سایت Carsonified از تضاد جهت اثرگذاری استفاده می‌کند. این سایت از متن و عکس‌های درشت و بالعکس و ترکیب رنگ‌های متضاد بهره برده است. همانطور که در زیر می‌بینیم جلد مجله‌ی Proximity، از عکس چندین قایق شناور کوچک در درون دریای آبی تیره استفاده کرده است که تضاد زیبایی را به تصویر کشیده است. ۲. تضاد به سازماندهی اطلاعات کمک می‌کند. استفاده از تضاد نه تنها سبب جذابیت هر چه بیشتر طراحی می‌شود بلکه به هدف و سازماندهی اسناد وضوح بهتری می‌بخشد. در مجله‌ی منتشرشده در زیر، Studio8 از قوانین تضاد، تعادل (Balance) و مجاورت (Proximity) برای ایجاد صفحاتی غیر معمول و چشم‌گیر استفاده کرده است. عناوین درشت مشکی تضاد جالبی با متن ظریف روشن ایجاد کرده‌اند. در ادامه‌ی مجله، Studio8 صفحات را به دو قسمت تقسیم کرده است که از لحاظ ظاهری کاملا متضاد هم هستند. هر صفحه اطلاعاتی را در مورد محصول‌های جداگانه‌ای می‌دهد که به هم مرتبط هستند. مفهوم این ارتباط توسط علامت '&' ادا شده است. ۳. تضاد سبب ایجاد تمرکز می‌شود. آگهی‌ها و تبلیغات مشهور iPod از مفهوم تضاد به صورت کاملا حرفه‌ای برای متمرکز ساختن توجه بینندگان به پخش‌کننده‌ی موسیقی استفاده کرده است. در این تبلیغات یک شخصیت تیره رنگ بر روی زمینه‌ی رنگی قرار گرفته است. iPod و هدفون‌ به رنگ سفید هستند که در مقابل شخصیت تیره رنگ و زمینه‌ی رنگی خود را به خوبی نشان می‌دهند. طراحی برای این بطری سفارشی بر پایه‌ی تضاد بین متن سفید و رنگ قرمز تیره‌ی نوشیدنی است که به عنوان یک کادوی کریسمس، منحصر به فرد است. مواردی که هنگام افزودن تضاد به طرح‌های خود باید به آن‌ها فکر کنید: ۱. چگونه تضاد را ایجاد میکنید؟ از طریق بافت، تایپوگرافی، رنگ یا شکل؟ ۲. اگر می‌خواهید از طریق تایپوگرافی تضاد ایجاد کنید، کدامیک از فونت‌ها را استفاده می‌کنید؟ آیا آن‌ها بسیار متفاوت هستند یا فقط کمی با هم تفاوت دارند؟ گزینههای فونت خود را با دقت بررسی کنید اما به خاطر داشته باشید که متن قابل خواندن باشد. ۳. آیا تضاد به کار رفته، ایده‌ی طراحی شما را تقویت می‌کند؟ پی‌نوشت: مقالات و دوره‌های سایت Sitepoint پیشنهاد می‌شوند. با ایجاد حساب کاربری امکانات جذابی در اختیارتان قرار خواهد گرفت.
  12. قاسم رمضانی منش

    سلام؛ خوش‌آمدید، لطفاً ده-دقیقه وقت بگذارید و اسناد زیر را جهت فعالیت در انجمن مطالعه بکنید : قوانین نگارشی جهت نشر محتوا سند نحوه‌ی پرسش و پاسخ هوشمندانه سیستم‌عامل گنو/لینوکس، یک سیستم‌عامل آزاد - متن‌باز می‌باشد، که می‌توانید به رایگان آن را دریافت و استفاده کنید : سیستم‌عامل گنو/لینوکس و جنبش نرم‌افزار آزاد آزاد به چه معنی است ؟ نسخه‌های متعدد و زیادی از سیستم‌عامل‌های گنو بر پایه کرنل لینوکس موجود می‌باشد که می‌توانید هرکدام را به رایگان دریافت و استفاده بکنید : دریافت سیستم‌عامل آزاد دبین. جهت شروع آشنایی کار با این سیستم‌عامل‌ها می‌توانید از این دورهٔ آموزشی آقای‌میر‌میرانی شروع کنید : دورهٔ آموزشی مدرک LPIC-1 نیازی به بارگیری SDK و NDK دوباره نیست، ولی برای JDK بسیار بهتر می‌باشد که از طریق مدیربستهٔ توزیعتان اقدام به نصب آن کنید که اختلالی پیش نیاید. مطمئن شوید که این سند را نیز مطالعه کردید : کیوت برای اندروید.
  13. سلام دوستان، نظرتان درمورد گنو/لینوکس چیست ؟ می‌خواهم کار با این سیستم‌عامل را شروع کنم، پیشنهادتان چیست ؟ کیوت توی ویندوز با خروجی اندروید خیلی اذیت میکنه، اول که خروجی نمیداد، بعد که درستش کردم، الان از طریق اندروید استادیو SDK رو آپدیت کردم، دیگه خروجی نمیده! کلا کیت های اندروید را بسته. اول SDK رو آپدیت کردم، دیدم دیگه جواب نمیده بعد NDK رو آپدیت کردم، مشخصات : NDK 20 SDK 29 JDK 1.8 - اگر بخوام برم سراغ گنو/لینوکس باید دوباره کل SDK و NDK و JDK برای لینوکس دانلود کنم؟
  14. Saman

    سلام دوستان آیا کسی برای این مشکل راه حلی داره ؟
  15. Seyyed Ali Mohammadiye

    آشنایی با سرویس میزبانی صفحات گیت هاب (Github Pages)

    سلام وقت بخیر, گیت هاب پیج گیتهاب پیج یک سرویس میزبانی وب است که توسط 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 هم مطالعه کنید. سپاس سید علی محمدیه
  16. با سلام وقت بخیر, در این مطلب میخواهیم در مورد روش کارکرد پیام رسان ها بیشتر بدانیم و با یکدیگر کد یک پیام رسان ساده را پیاده و بررسی کنیم. طرز کار کرد پیام رسان در نظر داشته باشید که هر پیام رسانی که بر ساختار ها پیاده شده باشد از دو قسمت تشکیل شده است. نرم افزار اصلی برای مدیریت درخواست ها (سرور) نرم افزار برای کاربران (کاربر) به نرم افزار اولی سمت 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; } این پروژه آزمایشی بصورت رایگان و اوپن سورس در اینترنت بخصوص اینجا وجود دارد و می توانید آنرا مستقیما بصورت کامل دانلود کنید. با تشکر, سید علی محمدیه
  17. جدیدا
  18. قاسم رمضانی منش

    خواهش‌می‌کنم؛ راحت‌باشید درصورتی‌که اشتباهی یافتید اصلاح کنید.
  19. amirb

    مشکل از ndk بود. از نسخه ۱۸ استفاده کردم مشکل حل شد.تشکر
  20. سید محمد عباسی

    اولین پاراگراف رو که خوندم لذت بردم و پیشاپیش تشکر می‌کنم . ممنون از مقالات خوبتون ❤ .
  21. کامبیز اسدزاده

    سلام، لطفاً مشخصات مرتبط با NDK, SDK و JDK را جهت بررسی بیشتر ارسال کنید. باید دقت کنید که نسخه‌ی کیوت ۵.۱۱ به بعد باید از نسخه‌های JDK 8.x و همچنین SDK 26 و NDK r18 استفاده کنید. دقت کنید در صورتی که از کیوت ۵.۱۱ و یا سری ۵.۱۲ استفاده می‌کنید باید نسخه‌ی NDK حتماً روی r18 باشه (تحت کامپایلر Clang) تا به درستی هدر‌های مورد نیاز رو شناسایی بکنه.
  22. سید محمد عباسی

    آقای اسدزاده ممنونم ❤ ولی حِسم به مایکروسافت یه جوری شد ☺
  23. با سلام و عرض تسلیت به مناسبت شهادت مولای متقیان امام علی علیه السلام. من روی اوبونتو ۱۸.۰۴ کیوت ۵.۱۲.۳ نصب کردم و همه ی پیش نیاز های برنامه نویسی اندروید هم درست هست. ولی وقتی می خوام بیلد کنم و خروجی apk بگیرم پیغام زیر رو دریافت می کنم: /Qt/5.12.3/android_armv7/include/QtCore/qglobal.h:50: error: 'assert.h' file not found # include <assert.h> ^ نکته: خروجی دسکتاپ راحت گرفته میشه و کار می کنه ولی اندروید نه.
  24. سلام بر دوستان گرامی. فرض کنین ما در برنامه ای که با زبان C++ قراره نوشته بشه محاسبات ماتریسی داریم که بزرگ و زمانبر هست و برای انجام اون از کتابخانه Eigen استفاده میکنیم. چنانچه بخوایم پس از اتمام محاسبات ماتریسی، نتیجه رو در رابط کاربری که داریم نمایش بدیم اعم از نمایش به شکل Spreadsheet و یا رسم روی نمودار و... مجبوریم اشیا ساخته شده با Eigen رو به آرایه های مورد استفاده در Qt نظیر QVector یا QList و... تبدیل کنیم. حالا اگر تکرار این محاسبات زیاد باشه این تغییر نوع اشیا کاملا روی سرعت اجرای برنامه تاثیر میزاره حالا زبان C++ برای حل این نوع از مشکلات که برنامه نویس رو مجبور به تغییر نوع متغیر برای استفاده در هر نوع کتابخانه میکنه چه فکری کرده؟ آیا راهی هست که بشه مستقیم بین این کتابخانه ها ارتباط برقرار کرد به طوری که: نیاز به تغییر در کد منبع هیچ یک از کتابخانه‌ها نباشه چون این کار زمان بر هست و نیاز به تجربه بالایی در کار با اون کتابخانه خاص داره و قراره محتویات اون دستکاری و قاعدتا کتابخانه مجددا به خاطر اون تغییر، همگردانی بشه. آیا از وراثت میشه برای یکپارچه سازی بین اشیا موجود در کتابخانه های مختلف استفاده کرد؟ یا راه حل دیگه ای وجود داره؟ مثال Eigen و Qt صرفا برای بیان بهتر این مشکل آورده شد. سپاس گزارم.
  25. کامبیز اسدزاده

    سلام، سمت سرور یک مقدار رو ارسال کن تحت متد GET یا POST مقدارش رو بررسی کن و اگه کمتر یا بیشتر بود بر اساس اون به شما اعلام وضعیت کنه. روش اعلان رو هم تحت نوتیفیکیشنی چیزی انجام بده.
  26. Saman

    برای انجام این کار باید یک اعلان برای کاربر نمایش داده بشه تا کاربر از نسخه جدید اطلاع پیدا کنه این رویداد چطوری ایجاد میشه ؟؟ ممنون میشم منابع معرفی کنید .
  27. قاسم رمضانی منش

    استفاده از MD

    زبان نشانه‌گذاری 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 | :( خروجی تمام نمونه‌های بالا : استفاده از قالب‌های گفته شده، بستگی به پیاده‌سازی موتور رندر ویرایشگری دارد که شما از آن استفاده می‌کنید، چرا که ممکن است تمام قابلیت‌ها را پیاده‌سازی نکرده‌باشد. موفق‌وپیروز باشید.
  28. قاسم رمضانی منش

    عیب‌یابی، دیباگ‌کردن با GNU Debugger

    بارها بوده که برنامه‌‌ای را نوشته‌ایم، روند کامپایل و اجرا به خوبی و خوشی انجام می‌شود. امّا در مرحلهٔ اجرای برنامه، خروجی‌های نامناسبی پدیدار می‌شود. که متأسفانه چیزی نیستند که ما می‌خواهیم. خب برای حل این مشکل دو راه پیش‌رو می‌باشد : بازبینی کد و انجام تست برای یافتن محل مشکل. استفاده از ابزارهای خطایابی (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-- لینک منبع را برای ادامهٔ داستان دنبال کنید :). موفق و پیروز باشید.
  1. نمایش فعالیت های بیشتر
×