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

پرچمداران

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

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

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


    • امتیاز

      468

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

      368


  2. بهنام صباغی

    بهنام صباغی

    مدیران مرجع


    • امتیاز

      66

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

      82


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

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

    مدیران مرجع


    • امتیاز

      51

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

      89


  4. الهه انصاری

    الهه انصاری

    طراحان


    • امتیاز

      25

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

      13



مطالب محبوب

در حال نمایش مطالب دارای بیشترین امتیاز از زمان جمعه, 4 خرداد 1397 در همه بخش ها

  1. 19 امتیاز
    مراحل ساخت برنامه‌ در زبان سی‌پلاس‌پلاس پیش نویس ۰.۶ قبل از هر چیز به اینفوگرافی زیر توجه کنید که مراحل ساخت برنامه در سی‌پلاس‌پلاس را نشان می‌دهد. مقدمه‌ای بر همگردانی (کامپایل) و اتصال (لینک کردن) این سند مرور مختصری در رابطه با مراحل را برای شما فراهم می‌کند تا به شما در درک دستورات مختلف برای تبدیل و اجرای برنامه‌ی خودتان کمک کند. تبدیل مجموعه‌ای از فایل‌های منبع و هدر در سی‌پلاس‌پلاس به یک فایل خروجی و اجرایی در چندین گام (به طور معمول در چهار گام) پیش‌پردازنده (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 خواهد بود. شرح کامل فرایند ساخت فایل اجرایی اکثر پروژه‌ها دارای مجموعه‌ای از فایل‌های هدر سی++ هستند، که امکان ماژولار شدن در آن را فراهم می‌کند و مجموعه‌ای از آن می‌تواند به عنوان بخش‌های کوچکی از برنامه محسوب شوند. برای ساخت چنین پروژه‌هایی هر فایل سی‌پلاس‌پلاس باید کامپایل شود و سپس فایل‌های ساخته شده در قالب شیء (آبجکت) باید همراه توابع و کتابخانه‌های دیگر لینک (پیوند) شوند. البته هر گام از مراحل کامپایل شامل یک مرحله پیش‌پردازنده است که دستورالعمل # عمل تغییرات و اصلاحیه‌ها را در فایل متن اعمال می‌کند. شکل زیر فرایند ساخت چند فایل به صورت همزمان را نشان می‌دهد:
  2. 8 امتیاز
    پیشنهادات و ملاحظات در عملکرد و کارآیی همانطور که می‌دانید تولید و توسعه‌ی یک نرم‌افزار با سرعت و کارآیی بالا یکی از مزایایی مهارتی توسعه‌دهندگان است که به تنهایی می‌تواند به عنوان یک فاکتور بسیار مهم مطرح شود. بنابراین در صورتی که شما محصولی را در قالب وب، موبایل یا دسکتاپ تولید می‌کنید باید به آن توجه داشته باشید که سرعت اجرایی برنامه‌ی شما در زمان استارتاپ و بعد در مراحل پردازشی و فرایند‌های دیگر باید مورد قبول باشد. در این میان برنامه‌نویسان سی++ می‌دانند که باید در مدیریت حافظه و دیگر موارد با دقت بیشتری عمل کنند تا بتوانند به حداقل نشتی در حافظه (حتی فاقد نشتی در آن) و در نهایت رسیدن به حداکثر سرعت پردازشی توجه داشته باشند. با توجه به این موضوع که کتابخانه‌ی Qt به عنوان یکی از کتابخانه‌های این زبان بشمار می‌آید؛ بسیاری از توسعه‌دهندگان درگیر توسعه‌ی بخش فرانت‌اِند نیز می‌شوند. بنابراین باید توجه داشت در زمان توسعه رابط کاربری مبتنی بر فناوری Qt Quick مواردی را جهت افزایش کارآیی سرعت و هماهنگی کامل با سی++ را در نظر بگیرند چرا که بدون در نظر گرفتن فاکتور‌های زمانی نتیجه‌ی آن بسیار بد خواهد بود. شما به عنوان یک توسعه دهنده نرم‌افزار، باید برای رسیدن حداکثر فریم ریت ۶۰ در موتور رندرینگ تلاش کنید. این میزان بدین معنی است که بین هر فریم که در میان آن‌ها می‌تواند پردازش انجام شود، تقریباً ۱۶ میلی ثانیه وجود دارد که شامل پردازش مورد نیاز برای بارگذاری‌های اولیه به سمت سخت‌افزار گرافیکی می‌باشد. در عمل، این به این معنی است که توسعه‌دهنده نرم‌افزار باید: هر کجا که ممکن است از روش ناهمزمان (Asynchronous) در بخش برنامه‌نویسی رویدادمحور (Event-driven programming) استفاده کند. در بخش‌هایی که به میزان قابل توجهی شامل پردازش می‌شوند از وُرکر‌ترد‌ (worker threads) استفاده کند. هرگز حلقه رویداد را به صورت دستی برای چرخش و ادامه تنظیم نکند. هرگز بیش از چند میلی ثانیه برای هر فریم در بلوک توابع صرف نکند. عدم انجام این کار‌ها باعث پَرش در فریم‌ها می‌شود که تاثیر بسیار جدی در تجربه‌ کاربری دارد. نکته: الگویی که وسوسه انگیز است اما نباید هرگز از آن استفاده شود، چرا که یک‌‌ متد QEventLoop و یا صدا زدن یک متد دیگر مانند QCoreApplication::processEvent به منظور جلوگیری از مسدود شدن در یک بلوک کُد سی++ که در QML استفاده شده است به کار گرفته می‌شود. این خطرناک است، زیرا زمانی که یک حلقه رویداد در هندلر سیگنال و یا پیوند (Binding) وارد می‌شود، موتور QML همچنان برای اجرای سایر پیوندها، انیمیشنها، ترنسیشن‌ها و غیره می‌پردازد. این اتصالات می‌توانند باعث عوارض‌های جانبی شوند. پروفایلینگ (Profiling) مهمترین نکته این است که: از ابزار QML Profiler که بخشی از محیط توسعه یکپارچه‌ی نرم‌‌افزار Qt Creator است استفاده کنید. زیرا دانستن زمانی که در یک برنامه صرف می‌شود به شما امکان این را می‌دهد تا بر قسمت یا بخشی که در آن مشکل وجود درد تمرکز سریع‌تر و بهتری داشته باشید. برای نحوه‌ی استفاده از این ابزار به کتابچه‌ی راهنمای خود کیوت کریتور مراجعه کنید. تعیین کردن اینکه اغلب کدام پیوند (اتصال) در حال اجرا است، یا کدام یک از توابع شما بیشترین زمان را برای اجرا صرف می‌کنند، به شما این امکان را می‌دهد که تصمیم بگیرید که آیا شما نیاز به بهینه سازی آن بخش از کُدها را دارید یا نیاز خواهد بود آن بخش از کد خود را مجددا پیاده سازی کنید تا عملکرد آن بهتر شود یا خیر. به طور کلی تلاش برای بهینه سازی بدون استفاده از ابزار QML Profiler احتمالاً باعث بهبود‌های جزئی و غیرقابل توجه در عملکرد خواهد شد. کُد جاوا اسکریپ (JavaScript) بیشتر برنامه‌های QML مقدار زیادی از کدهای جاوا اسکریپت در درون خود خواهند داشت. که به شکل توابع پویا، مدیریت سیگنال و یا عبارت‌های مربوط به پراپرتی‌ها (مشخصه، اموال) را در اختیار دارند. به طور کلی این یک مشکل نیست. با تشکر از برخی از بهینه سازی‌های موتور QML، مانند آنهایی که به کامپایلر منتقل می‌شوند، می‌تواند (در بعضی موارد استفاده شده) حتی سریع‌تر از فراخوانی‌های سمت سی++ باشد. با این حال، باید اطمینان حاصل شود که پردازش غیر ضروری به طور تصادفی اتفاق نیفتاده باشد. اتصالات (Binding) دو نوع اتصال در QML وجود دارد: اتصالات بهینه شده و غیر بهینه شده. ایده‌ی خوبی است که عبارات مربوطه را به همان اندازه ساده‌تر حفظ کنید. زیرا موتور QML برای ارزیابی عبارات از یک ارزیابی ساده و بهینه شده استفاده می‌کند که می‌تواند مشخص کند تا نیازی به تغییر آن در محیط جاوا اسکریپت نباشد. این اتصالات بهینه سازی شده بسیار کارآمدتر از اتصالات پیچیده تر (غیر بهینه سازی شده) هستند. الزامات اساسی نیز برای این گونه اتصال‌ها این است که اطلاعات نوع هر نماد که به آن دسترسی دارد باید در زمان کامپایل شناخته شده باشد. عواملی که باعث جلوگیری از به حداکثر رساندن بهینه سازی در عبارات اتصالات می‌شود: تعریف متغیرهای واسط توسط جاوا اسکریپت دسترسی به خواص var صدا زدن توابع جاوا اسکریپت ساخت (آغاز یا خاتمه یافتن) و یا تعریف توابع در میان عبارت‌ اتصالات دسترسی به خواص (پراپرتی‌های) خارج از محدوده ارزیابی فوری نوشتن درمورد سایر عوامل به عنوان عوارض جانبی توجه داشته باشید، زمانی اتصالات سریعتر می‌شوند که نوع اشیاء و خواصی که با آنها کار می‌کنند مشخص شده باشند. این به این معنی است که، خواص غیرنهایی (non-final) ممکن است در بعضی موارد کُند‌تر باشد. (جایی که ممکن است یک خاصیت تغییر کرده باشد). محدوده‌هایی ای که برای ارزیابی فوری می‌توان در نظر گرفت قسمت زیر هستند: ارزیابی در خاصیت‌ عبارت‌های دامنه‌ی یک شیء (برای عبارت‌های اتصال، این است که شیء به کدام یک از خاصیت‌های اتصال تعلق دارد). ارزیابی در شناسه‌های هر یک از اشیاء موجود در کامپوننت (جزء) ارزیابی در خاصیت‌های ریشه‌ی آیتم در یک کامپوننت (جزء) شناسه‌های اشیاء از دیگر کامپوننت‌ها و خاصیت‌های هر یک از اشیاء، مانند نمادهای تعریف شده و یا وارد شده از طرف جاوا اسکریپت در محدوده ی ارزیابی فوری نیستند. به این ترتیب اتصالاتی که به این موارد مربوط هستند نمی‌توانند بهینه سازی شوند. نکته: توجه داشته باشید که اگر نمی‌توانید اتصالات مورد نظر خود را توسط موتور QML بهینه‌سازی کنید، در این صورت باید آن را در محیط کامل جاوا اسکریپت بهینه سازی نمایید. نوع تبدیل (Type-Conversion) یک مزیت عمده برای استفاده از جاوا‌ اسکریپت این است که در اغلب موارد، هنگامی‌‌که به یک ویژگی از نوع QML دسترسی پیدا می‌شود‌، یک شیء جاوا اسکریپت با منبع حاوی سی‌پلاس‌پلاس پایه (یا یک مرجع آن) ایجاد می‌‌گردد. در بیشتر موارد، این عمل به‌نسبت کم‌هزینه است، اما در سایر موارد می‌تواند بسیار پر‌هزینه باشد. یک مثال از پر‌هزینه بودن آن، ارجاع کردن QVariantMap و Q_PROPERTY به ویژگی نوع QML است. لیست‌ها نیز می‌توانند هزینه‌بالایی داشته‌ باشند. اگرچه دنباله‌ی انواع خاص (QList از int، qreal، bool، Qstring و QUrl) باید کم‌هزینه باشد. تبدیل انواع دیگر لیست هزینه‌ی زیادی دارد (ایجاد آرایه‌ی جدید جاوا اسکریپت و افزودن تک به تک انواع جدید، با تبدیل هر نوع از نمونه‌ی‌ نوع سی‌پلاس‌پلاس به مقدار جاوا اسکریپت). رفع سازی خواص‌ها تفکیک پذیری در خواص (پراپرتی‌ها) زمان بر است. در حالی که در بعضی از موارد نتیجه آن می‌تواند ذخیره شده و دوباره مورد استفاده قرار بگیرد. بهتر است همیشه از انجام کارهای غیر ضروری جلوگیری شود. در مثال زیر، ما یک بلوک کد داریم که اغلب اجرا می‌شود (در این مورد، آن شامل یک حلقه صریح است؛ اما برای مثال می‌توان آن را با یک عبارت پیوند و مورد ارزیابی قرار داد). در این مثال مشکل را با در نظر گرفتن شناسه "rect" و خاصیت رنگ "color" آن را چندین بار تغییر و تولید می‌کنیم. این مثال کاری که می‌خواهیم را انجام می‌دهد، اما روش بهینه شده ای نیست بنابراین مثال زیر بسیار بد خواهد بود: import QtQuick 2.3 Item { width: 400 height: 200 Rectangle { id: rect anchors.fill: parent color: "blue" } function printValue(which, value) { console.log(which + " = " + value); } Component.onCompleted: { var t0 = new Date(); for (var i = 0; i < 1000; ++i) { printValue("red", rect.color.r); printValue("green", rect.color.g); printValue("blue", rect.color.b); printValue("alpha", rect.color.a); } var t1 = new Date(); console.log("Took: " + (t1.valueOf() - t0.valueOf()) + " milliseconds for 1000 iterations"); } } برای اینکه کد فوق بهینه سازی شود بهتر است به صورت زیر باشد: import QtQuick 2.3 Item { width: 400 height: 200 Rectangle { id: rect anchors.fill: parent color: "blue" } function printValue(which, value) { console.log(which + " = " + value); } Component.onCompleted: { var t0 = new Date(); for (var i = 0; i < 1000; ++i) { var rectColor = rect.color; // resolve the common base. printValue("red", rectColor.r); printValue("green", rectColor.g); printValue("blue", rectColor.b); printValue("alpha", rectColor.a); } var t1 = new Date(); console.log("Took: " + (t1.valueOf() - t0.valueOf()) + " milliseconds for 1000 iterations"); } } فقط این تغییر ساده باعث بهبود عملکرد قابل توجهی می‌شود. بنابراین، توجه داشته باشید که کد بالا را می‌توان حتی بهبود بیشتری داد (از آنجا که ویژگی مورد نظر هرگز در طول پردازش حلقه تغییر نکرده است)، با بالا بردن دقت خارج از حلقه، به صورت زیر است: import QtQuick 2.3 Item { width: 400 height: 200 Rectangle { id: rect anchors.fill: parent color: "blue" } function printValue(which, value) { console.log(which + " = " + value); } Component.onCompleted: { var t0 = new Date(); var rectColor = rect.color; // resolve the common base outside the tight loop. for (var i = 0; i < 1000; ++i) { printValue("red", rectColor.r); printValue("green", rectColor.g); printValue("blue", rectColor.b); printValue("alpha", rectColor.a); } var t1 = new Date(); console.log("Took: " + (t1.valueOf() - t0.valueOf()) + " milliseconds for 1000 iterations"); } } پیوند خاصیت‌ها (Property Binding) در صورتی که خواص مرجع هر یک از اتصالات تغییر یابند، به آن خاصیت متصل دوباره ارزیابی خواهد شد. به همین ترتیب، عبارات خواص باید به همان اندازه ساده باشند. اگر شما یک حلقه دارید که در آن پردازشی را انجام می‌دهید، اما تنها نتیجه نهایی پردازش مهم است، اغلب بهتر است که موقتاً آن را به‌روز کرده و در صورت نیاز آن را برای به‌روز رسانی یک خاصیت مورد نیاز اختصاص دهید. این کار به منظور جلوگیری از ارزیابی‌های مجدد بسیار مفید خواهد بود. مثال ذکر شده زیر این نکته را نشان می‌دهد که بسیار بد پیاده سازی شده است: import QtQuick 2.3 Item { id: root width: 200 height: 200 property int accumulatedValue: 0 Text { anchors.fill: parent text: root.accumulatedValue.toString() onTextChanged: console.log("text binding re-evaluated") } Component.onCompleted: { var someData = [ 1, 2, 3, 4, 5, 20 ]; for (var i = 0; i < someData.length; ++i) { accumulatedValue = accumulatedValue + someData[i]; } } } حلقه ای که در رویداد onCompleted می‌باشد، موجب می‌شود تا خاصیت متن text به تعداد ۶ بار مجدداً مورد ارزیابی قرار بگیرد (که در نتیجه هر گونه پیوند مرتبط با مقدار متن متکی است) همچنین کنترلر سیگنال onTextChanged هر یک را مجدداً ارزیابی می‌کند و این موجب می‌شود متن هر بار نمایش داده شود. این امر ضروری نیست چرا که هدف ما دسترسی به ارزش نهایی آن خواهد بود. روش پیشنهادی به صورت زیر خواهد بود: import QtQuick 2.3 Item { id: root width: 200 height: 200 property int accumulatedValue: 0 Text { anchors.fill: parent text: root.accumulatedValue.toString() onTextChanged: console.log("text binding re-evaluated") } Component.onCompleted: { var someData = [ 1, 2, 3, 4, 5, 20 ]; var temp = accumulatedValue; for (var i = 0; i < someData.length; ++i) { temp = temp + someData[i]; } accumulatedValue = temp; } } بارگذاری‌ها و منابع مرتبط به تصاویر تصاویر بخش مهمی از رابط کاربری هستند. متأسفانه، به دلیل زمان بارگیری آنها، میزان حافظه ای که مصرف می‌کنند نیز قابل توجه است. بنابراین توجه داشته باشید که در صورتی که اپلیکیشن شما شامل تصاویری با اندازه بزرگتر هستند، اما شما آن را در اندازه‌ی کوچکتر از واقعی خود نشان می‌دهید. در این صورت بهتر است اندازه سورس آن را نیز مشخص کنید که در خاصیت "sourceSize" مشخص می‌شود. این کار باعث می‌شود تا مطمئن شویم عنصور تولید شده با مقیاس کوچکتری در حافظه نگهداری می‌شود. البته مراقب این نیز باشید که تغییر اندازه سورس موجب می‌شود تصویر دوباره بارگیری شود. بارگیری غیر‌همزمان (Asynchronous) معمولاً برنامه‌های زیبا و با کیفیت دارای تصاویر با کیفیت نیز هستند، بنابراین لازم است تا اطمینان حاصل شود که بارگیری یک تصویر یک رشته از UI را بلوک یا مسدود نمی‌کند. خاصیت asynchronous در نوع QML Image را با مقدار true تنظیم کنید تا بارگیری غیرهمزمان از تصاویر بر روی سیستم فایل‌های محلی بارگیری شود. این کار از وارد کردن فشار بر روی تصاویر و رابط کاربری جلوگیری خواهد کرد که نتیجه‌ی آن حفظ زیبایی رابط کاربری شما در زمان بارگذاری تصاویر خواهد شد. نکات ریز اما مهم از سایه پردازی‌ها و تصاویری که در حین زمان اجرا در ترکیب تصاویر شما ایجاد می‌شوند دوری کنید. توجه داشته باشید که در صورت عدم نیاز از خاصیت smooth استفاده نکنید. فعال سازی این خاصیت موجب تولید تصاویر صاف و با کیفیت می‌شود که در سخت افزا‌های قدرتمند توصیه می‌شود. در سخت افزار‌های ضعیف تولید تصاویر صاف زمانبر خواهد بود و در صورتی که اندازه‌ی تصویر واقعی نباشد بر روی نتیجه‌ی آن تاثیری نخواهد داشت. از نقاشی و یا رسم طرح بر روی یک ناحیه به صورت پشت سر هم اجتناب کنید. از نوع Item به عنوان عنصر ریشه به جای Rectangle استفاده کنید. عناصر موقعیت با لنگر استفاده از لنگرها (anchors) به جای اتصالات (bindings) به یک آیتم موقعیت نسبتاً تاثیر بیشتری خواهد داشت. برای مثال برای اتصال موقعیت rect2 به rect1 را توجه کنید: import QtQuick 2.3 .... Rectangle { id: rect1 x: 20 width: 200; height: 200 } Rectangle { id: rect2 x: rect1.x y: rect1.y + rect1.height width: rect1.width - 20 height: 200 } .... این کار را می‌توان توسط لنگر‌ها به طور بسیار موثری انجام داد: import QtQuick 2.3 .... Rectangle { id: rect1 x: 20 width: 200; height: 200 } Rectangle { id: rect2 height: 200 anchors.left: rect1.left anchors.top: rect1.bottom anchors.right: rect1.right anchors.rightMargin: 20 } .... تنظیم موقعیت توسط اتصالات (با اختصاص دادن عبارت‌های اتصال به محورهای x و y طول و ارتفاع به عنوان خواص یک شیء به جای استفاده از لنگر‌ها) هرچند به حداکثر انعطاف پذیری اجازه می‌دهد اما نتیجه تولیدی آن نسبتاً کُند است. در نظر داشته باشید که اگر شیء شما به صورت داینامیک (پویا) تغییر پیدا نمی‌کند، روش مناسب تغییر موقعیت استفاده از خاصیت‌های y، x‌، width و height می‌باشد که متناسب و وابسته به والد خود می‌باشد. در صورتی که می‌خواهید وابستگی ایجاد نشود بهتر است از لنگر‌ها استفاده کنید. در مثال زیر، اشیاء مستطیل (Rectangle) فرزند در یک مکان مشابهی قرار گرفته اند، اما کدهای مرتبط به لنگرها (anchors) نشان میدهد که موقعیت آن شیء به صورت ثابت مقدار دهی شده است. import QtQuick 2.3 Rectangle { width: 60 height: 60 Rectangle { id: fixedPositioning x: 20 y: 20 width: 20 height: 20 } Rectangle { id: anchorPositioning anchors.fill: parent anchors.margins: 20 } } مدلها و نمایش‌ها اکثر برنامه های کاربردی (Application) حداقل از یک مدل (Model) تغذیه داده را به نمایه (View) ارسال می‌کنند. بعضی از مواردی که توسعه دهندگان برای به حداکثر رساندن عملکرد به آنها باید توجه داشته باشند وجود دارد. مُدل‌های سفارشی سمت سی‌پلاس‌پلاس این بسیار خوب است که شما مدل‌های داده‌ای سفارشی خود را سمت ++C برای استفاده ار نمایه‌ی QML بنویسید. اما باید توجه داشته باشید این کار به شدت بستگی به کاربرد آن در موقعیت خودش را دارد که برخی از دستورالعمل های کلی به شرح زیر هستند: تا جایی که ممکن است ناهمزمان باشد. تا جای ممکن بصورت ناهمزمان، تمام پروسه‌های پردازشی را در یک نخ با (اولیت پایین) انجام دهید. عملیات بک‌اند را به صورت دسته ای انجام دهید تا (به طور بالقوه آهسته) عمل‌های I/O و IPC به حداقل برسد. از یک پنجره مجزا برای نتایج کَش که پارامترهایش به پروفایلینگ کمک می‌کنند، استفاده کنید. این مهم است که توجه داشته باشیم استفاده از یک نَخ (Thread) کارآمد با کمترین اولویت توصیه میشود تا خطر قحطی (starving) را در نخ های رابط کاربری به حداقل برساند. همچنین به یاد داشته باشید که مکانیزم هماهنگ سازی و قفل کردن می‌تواند عامل مهمی برای عملکرد آهسته باشد، بنابراین لازم است از اعمالِ قفل غیر ضروری جلوگیری (صرف نظر) شود. نوع لیست مُدل (ListModel) در QML کیوامال (QML) یک نوع ListModel ای را فراهم می‌کند که می‌تواند برای ارسال داده به یک نوع از ListView استفاده شود. این باید برای اکثر موارد مناسب باشد و تا زمانی که به درستی استفاده شود نسبتاً عملکرد درستی داشته باشد. تجمع در داخل یک نخ عناصر ListModel را می توان در یک نخِ کاری (با اولویت پایین) در JavaScript جابجا کرد. توسعه‌دهنده باید صریحاً متد sync() را که به عنوان یکی از خواص موجود در ListModel می‌باشد را داخل یک WorkerScript صدا بزند تا تغییرات همزمان با نَخ اصلی صورت بکیرد. جهت کسب اطلاعات بیشتر مستندات WorkerScript را مطالعه کنید. لطفاً توجه داشته باشید که با استفاده از یک عنصر WorkerScript یک موتور جاوا اسکریپت جداگاه ایجاد می‌شود (چرا که موتور جاوا اسکریپت در هر نَخ (Thread) می‌باشد). این باعث افزایش مصرف حافظه خواهد شد. چرا که عناصر متعدد (چندگانه) WorkerScript همه از یک نَخ استفاده خواهند کرد. بنابراین تاثیر شدیدی در حافظه‌ی استفاده شده توسط وُرکر دوم و سوم وارد خواهد کرد که در زمان اجرای برنامه و وُرکر اسکریپت اول تاثیر آن بسیار ناچیز خواهد بود. از نقش‌های (Roles) پویا استفاده نکنید عنصر ListModel در QtQuick 2 بسیار کارآمدتر از QtQuick است. بهبود عملکرد عمدتاً از پیش فرض‌های مربوط به نوع نقشها (Roles) در هر عنصر در یک مدل داده می‌شود. اگر نوع آن تغییر نکند، عملکرد ذخیره سازی به طور چشمگیری بهبود می‌یابد. اگر نوع قابلیت تغییر به صورت پویا از عنصر به عنصر دیگری را داشته باشد، بهینه سازی غیر ممکن شده و عملکرد (پرفرمنس - کارآیی) مدل به اندازه‌ی بزرگی بدتر و کُند‌تر خواهد شد. بنابراین به صورت پیشفرض حالت داینامیک غیر فعال بوده و توسعه‌دهنده خود باید خاصیت dynamicRoles را به عنوان یکی از خاصیت‌های ListModel فعال کند. البته به شدت پیشنهاد می‌شود برنامه‌ی خود را از اول طراحی کنید اما این قابلیت را فعال نکنید. نمایه‌ها (Views) توجه داشته باشید که نماینده (دلیگیتس یا delegates) باید به طور ساده حفظ شود. به اندازه‌ی کافی از انواع QML در این خاصیت‌های ضروری استفاده کنید. هرگونه قابلیت اضافه که بلافاصله مورد استفاده قرار نمی‌گیرد نیاز نیست. برای مثال اگر نیاز است اطلاعات بیشتری در موقع کلیک بر روی آیتم نمایش داده شود، نیاز نیست که همان لحظه ایجاد و بر روی آیتم نمایان شوند. نباید تا قبل از زمان نیاز ایجاد شوند. لیست زیر، خلاصه‌ی خوبی از مواردی است که باید هنگام طراحی بر روی خاصیت delegate در نظر داشته باشید: عناصری که کمتر در خاصیت delegate هستند، سریعتر می توانند ایجا شوند، و بنابراین سریعتر میتوانند در نمایه (view) مشاهده و اسکرول شوند. تعداد پیوندها (اتصالات) را در delegate به حداقل تعدادشان نگه داری کنید. از لنگرها (anchors) به جای اتصال برای موقعیتهای نسبی در یک delegate استفاده شود. از به کار گیری عناصر ShaderEffect در delegate اجتناب شود. هرگز خاصیت clip را در خاصیت delegate فعال نکنید. نکته: شما می‌توانید ویژگی cacheBuffer یک نمایه (view) را تنظیم کنید تا اجازه ایجاد و ارسال غیر مستقیم به delegate خارج از ناحیه قابل مشاهده را ایجاد کنید. استفاده از cacheBuffer برای delegate های نمایشی توصیه می شود. توجه داشته باشید که خاصیت delegate یک cacheBuffer اضافی را در حافظه نگه می‌دارد، بنابراین مقدار حاصل از استفاده cacheBuffer باید با استفاده از حافظه اضافی متعادل شود. توسعه‌دهندگان خود باید از معیارهای سنجش (بنچ مارک‌ها) برای یافتن بهترین مقدار مناسب برای استفاده را انجام دهند. چرا که افزایش حافظه ناشی از استفاده cacheBuffer می‌تواند در بعضی از موارد نادر، باعث کاهش نرخ فریم در هنگام پیمایش (اسکلرول) شود. جلوه‌های بصری فناوری کیوت کوئیک ۲ شامل چند ویژگی است که به توسعه‌دهندگان و طراحان اجازه می‌دهد تا رابط کاربری فوق العاده جذاب ایجاد کنند؛ تغییرات پویا (داینامیکی) و همچنین جلوه‌های بصری می‌توانند برای یک اثر بزرگ در برنامه‌‌ی کاربردی مورد استفاده قرار گیرد. اما هنگام استفاده از بعضی از ویژگی‌های QML، باید نکاتی را در نظر گرفت که می‌توانند دلالت بر کارآیی (پرفرمنس) را داشته باشند. انیمیشن‌ها به طور کلی، متحرک سازی (انیمیشن سازیِ) یک خاصیت (پراپرتی) می‌تواند سبب شود تا اتصالاتی که به یک خاصیت دیگر اشاره می‌کند را مجدداً مورد ارزیابی قرار گیرد. معمولاً این مورد مطلوب است، اما در برخی از موارد ممکن است بهتر باشد قبل از انجام متحرک سازی (انیمیشن سازی) اتصالات را غیر فعال کنید، و سپس آن انیمیشن را تکمیل کنید. از اجرای کدهای جاوا اسکریپت در طول یک انیمیشن اجتناب کنید. برای مثال، اجرای یک عبارت پیچیده جاوا اسکریپت برای هر فریم از انیمیشنِ یک خاصیت مانند x باید اجتناب شود. توسعه‌دهندگان باید در استفاده از انیمیشن‌های اسکریپتی دقت کنند، زیرا آنها در یک نخ اصلی اجرا می‌شوند (در نتیجه در صورتی که اجرا و تکمیل انیمیشن بیش از حد طول بکشد)، ممکن است فریم‌هایی را پرش کرده و موجب کاهش کارآیی اصلی آن‌ شود. ذَرات، پارتیکل (Particles)‌ ماژول Qt Quick Particles اجازه می‌دهد تا ذراتی را برای زیبایی هرچه بهتر رابط کاربری اضافه شود. با این حال، هر پلتفرم دارای قابلیت‌های سخت افزاری مختلفی است و ماژول Particles قادر به محدود کردن پارامترهای سخت افزاری شما نمی‌باشد. بنابراین زمانی که شما ذرات بیشتری برای تولید (رندر) می‌خواهید (هرچه بزرگتر می شوند)، سرعت بیشتری برای پردازش سخت افزار گرافیکی شما نیاز خواهد بود تا بتواند سرعت تولید آن‌ها را با نرخ ۶۰ فریم بر ثانیه اجرا کند. بنابراین ذرات بیشتر خواهان پردازنده‌های مرکزی سریعتری می‌باشند. بنابراین، این بسیار مهم است که تمام ذرات و اثرات آنها را بر روی پلتفرم‌های هدف خود با دقت آزمایش کنید، تا برای کالیبره کردن تعداد و اندازه ذرات قابل تولید با نرخ ۶۰ فریم به نتیجه قابل قبول برسید. لازم به ذکر است که برای جلوگیری از شبیه سازی غیر ضروری یک سیستم ذره را می‌توان غیر فعال کرد (به عنوان مثال عنصری را غر قابل مشاهده کنید) این کار باعث می‌شود تا شبیه سازی‌های غیر ضروری غیر فعال شوند. عملکرد سیستم ذرات با مقایس تعداد ذرات نگه داری می‌شود. پس از نمونه برداری از اثر مورد نظر، با کاهش مقدار ذرات، عملکرد آن را می‌توان بهبود داد. برعکس، اگر عملکرد آن به خوبی و در حد قابل قبول باشد، می‌توان تعداد ذرات را تا زمانی که در آن نقطه قرار می‌گیرد افزایش دهید(این کار باعث بهبود خواهد شد). همانند ShaderEffect، عملکرد سیستم ذرات تا حد زیادی وابسته به سخت افزار گرافیکی است که در حال اجرا می‌باشد. همچنین دقت کنید مقدار دهی loop در مصرف پردازنده بسیار موثر است، برای مثال اگر از SequentialAnimation استفاده می‌کنید، بهتر است مقدار loop را محدود در نظر بگیرید. در صورتی که مقدار آن برابر با Animation.Infinite باشد درصد قابل توجهی از پردازنده را درگیر خود خواهد کرد. کنترل طول عمر عنصر با تقسیم کردن یک برنامه به اجزای ساده، ماژولار، هرکدام از آنها در یک فایل QML منعکس می‌شوند، می‌توانید زمان راه اندازی برنامه را سریعتر و کنترل بیشتری از استفاده از حافظه را به دست آورده و تعداد عناصر فعال اما غیر قابل مشاهده را در برنامه خود کاهش دهید. به طور کلی سعی کنید برای هر ماژول یا بخشی از برنامه یک سند جداگانه از QML را فراهم کنید. مقدار‌دهی اولیه سریع (از روی تنبلی)! موتور QML برخی چیزها را به صورت هوشمندانه (یا زیرکانه) انجام می‌دهد تا اطمینان حاصل شود که بارگذاری و مقدار دهی اولیه اجزاء باعث نمی‌شود تا فریم‌ها از بین بروند. با این حال هیچ راهی برای کاهش زمان راه اندازی بهتر از این وجود ندارد که شما کارهایی را تا زمانی که به آن‌ها نیاز ندارید را انجام ندهید و از آن‌ها اجتناب کنید. این ممکن است با استفاده از اجزای پرکاربردی مانند نوع Loader یا ساخت کامپوننت (جزء)‌های پویا (Dynamic Object Creation) انجام شود. استفاده از بارکننده (لودر - Loader) لودر یک عنصری است که اجازه می‌دهد بارگذاری و تخلیه پویا بر روی اجزاء انجام شود. با استفاده از ویژگی "active" در Loader، مقدار دهی اولیه میتواند تا زمان لازم انجام شود. با استفاده از تابع setSource() مقدار دهی اولیه می‌تواند تامید شود. تنظیم خاصیت asynchronous بر روی مقدار true می‌تواند تاثیر بر روی بهبود عملکرد بر روی کامپوننت (جزء) در زمان نمونه سازی داشته باشد. استفاده از سازنده‌ی پویا توسعه دهندگان می‌توانند از یک تابع Qt.createComponent() برای ایجاد یک مولفه به صورت پویا در زمان اجرا از داخل جاوا اسکریپت استفاده کنند و سپس createObject() را برای نمونه سازی آن، فراخوانی کنند. نابود کردن عناصر استفاده نشده عناصری که مخفی هستند به عنوان فرزندی از عناصر غیر بصری محسوب می‌شوند. برای مثال زمانی که یک زبانه از TabBar یا TabWidget انتخاب شده است و نمایش داده می‌شود به لایلی باید سریع مقدار دهی اولیه شوند و زمانی که از آن به مدت طولانی استفاده نمی‌شود و این خود باعث بارگذاری اضافی است. بنابراین در صورت استفاده از این روش جهت جلوگیری از این هزینه مداوم در زمان اجرا ( که شامل تولید انیمیشن، اتصالات، رندرینگ و غیره...) به صورت خودکار حذف می‌شوند. نکته: یک آیتم بارگذاری شده با یک نوع عنصر Loader ممکن است توسط تنظیمات مجدد خاصیت source یا sourceComponent آزاد شود. در حالی که موارد دیگر ممکن است به صورت صریح با فراخوانی متد destroy() بر روی آنها منتشر شود. در بعضی از موارد ممکن است لازم باشد آیتم فعال را ترک کرده و یا حداقل آن را نامرئی کنید. این کار موجب می‌شود سرعت برنامه‌ی شما بهینه شود. تولید (رندرینگ) Rendering گرافیک صحنه‌ای که برای رندر در کیوت کوئیک ۲ استفاده می‌شود، رابط کاربری بسیار متحرک را به صورت فیزیکی در ۶۰ فریم بر ثانیه ارائه می‌کند. با این حال برخی چیزها به طور چشمگیری در عملکرد رندرینگ کاهش می‌یابند. توسعه دهندگان باید مراقب باشند تا از این مشکلات در هر جا که ممکن است اجتناب کنند که به برخی از آن‌ها اشاره شده است. کلیپ کردن (Clipping) کلیپ کردن به صورت پیشفرض غیرفعال است و توصیه می‌شود تنها زمانی که به آن نیاز دارید فعالش کنید. کلیپ کردن یک اثر بصری دارد، نه بهینه سازی! بنابراین این ویژگی پیچیدگی بیشتری را برای رندرینگ افزایش می‌دهد. اگر کلیپ فعال باشد، اجزای تولید شده خود و دیگر اجزای فرزندش را به محدوده خود متصل خواهد کرد. این باعث می‌شود رندرینگ در آن بخش متوقف شده و آن بخش از اشیاء مرتبط و منظم دیده شوند. نکته مهم این است که کلیپ کردن در داخل delegate بسیار نامناسب است و باید از این کار اجتناب شود. چرا که موجب کاهش کارآیی برنامه خواهد شد. نقاشی بیش از اندازه و عناصر نامرئی اگر شما عناصری دارید که توسط عناصر دیگری (مات) یا پوشانیده شده اند، بهتر است از خاصیت visible استفاده کرده و آن را به مقدار false تنظیم کنید. برای مثال در زبانه‌هایی که به صورت پیشفرض یکی از آن‌ها فعال هستند و زبانه‌های دیگر تا زمان انتخاب مخفی! در این صورت بهتر است آیتم‌های آن‌ها به از طرف والد آن‌ها مخفی شوند تا در زمان اجرا بابت نمایش مواردی که مخفی هستند هزینه‌ای نشود. شفاف در مقابل مات محتوای مبهم (مات) عموماً بسیار سریعتر از محتوای شفاف هستند. دلیل این امر این است که محتوای شفاف نیاز به ترکیب (مخلوط) کردن دارند و سیستم رندر کننده به مراتب می‌تواند نوع مات را بهتر بهینه سازی نماید. یک تصویر با یک پیکسل شفاف به طور کامل به عنوان یک نتیجه کاملاً شفاف تلقی می‌شود، حتی اگر عمدتاً مات باشد. همین امر برای نوع BorderImage با لبه های شفاف درست است. سایه‌ها نوع ShaderEffect باعث می‌شود که کد درون خطی GLSL را به صورت یکپارچه در یک برنامه Qt Quick با سربار بسیار کم قرار دهید. با این حال مهم است که بدانید، که قِطعه برنامه نیاز به اجرا برای هر یک از پیکسل‌ها در شکل تولید شده را دارد. هنگام استقرار بر روی یک سخت افزار با توان پایین، شیدر‌ها مقدار زیادی از پیکسل‌ها را پوشش می‌دهند برای جلوگیری از عملکرد ضعیف، باید یک قطعه شیدر را برای چند دستورالعمل جهت جلوگیری از پرفرمنس ضعیف نگهداری کنید. شیدرهای نوشته شده در GLSL اجازه می‌دهد تا تبدیلات و جلوه‌های پیچیده‌تری را تولید کنید. با این حال باید با دقت بسیاری از آنها استفاده کرد. استفاده از ShaderEffectSource باعث از پیش رندر شدن صحنه به FBO قبل از کشیده شدن آن می‌شود . این سربار اضافی می‌تواند بسیار پر‌هزینه باشد. تخصیص و جمع آوری حافظه مقدار حافظه‌ای که توسط یک برنامه اختصاص داده می‌شود و اینکه آن حافظه چگونه اختصاص داده می‌شود شامل ملاحظات بسیار مهمی هستند. صرف نظر از نگرانی‌های مربوط به خارج از حافظه در دستگاه‌های محدود به حافظه، تخصیص حافظه در پُشته به عنوان یک عمل محاسباتی نسبتاً گران می‌باشد. در این میان استراتژی‌های اختصاصی تخصیص حافظه می‌تواند باعث افزایش تقسیم داده‌ها در صفحات شود. خوشبختانه، جاوا اسکریپت از روش مدیریت حافظه خودکار در پُشته (Heap) در قالب GC پشتیبانی می‌کند که دارای برخی از مزیت‌ها است. اما با این حال دلالت بر مهم بودن برخی موارد دارد. یک برنامه نوشته شده در QML حافظه را از هر دو پُشته از سمت مدیریت حافظه تحت ++C و JavaScript مدیریت می‌کند. توسعه دهنده نرم‌افزار باید در رابطه با هر یک از آنها به منظور به حداکثر رساند پرفرمنس آگاه باشد. نکاتی برای توسعه‌دهندگان برنامه QML نکات و پیشنهادات موجود در این بخش تنها در قالب دستورالعمل هستند و ممکن است در همه شرایط قابل اجرا نباشند. با استفاده از معیارهای تجربی و بررسی بنچ مارک‌ها و همچنین آنالیز و همچنین با استفاده از ومعیار‌های تجربی برنامه خودتان بهترین تصمیم ممکن را بگیرید. اگر برنامه شما شامل نمایه (View) های متعدد (به عنوان مثال زبانه‌های چندگانه) می‌باشد اما در هر زمان تنها یکی از آنها مورد نیاز است در این صورت برای کم کردن حافظه مصرفی می‌توانید از روش مقدار‌دهی سریع (از روی تنبلی) استفاده کنید که در بالا به آن اشاره شده است. نابود سازی اشیاء ای که مورد استفاده قرار نمی‌گیرند اگر شما از روش ساده بارگیری کامپوننت‌ها و یا ساخت اشیاء به صورت پویا در طول یک عبارت جاوا اسکریپتی استفاده می‌کنید، بهتر است آنها را به صورت دستی توسط متد destroy() نابود کنید. این بهتر از آن است که منتظر باشید تا به صورت خودکار سیستم GC آن‌ها را جمع آوری و نابود کند. در زمانی که نیاز نیست به صورت دستی عمل GC (بازیافت حافظه) را انجام ندهید در اکثر موارد، دستیابی به مجموعه زباله‌ها به منظور دستیبای به آن نیست. زیرا این کار به مدت زیادی نخ‌های سمت رابط کاربری را مسدود می‌کند. این کار باعث می‌شود فریم‌ها و پرش‌هایی در انیمیشن‌های متحرک به وجود آید که باید از اینگونه هزینه ها اجتناب شود. به طور کلی باید توجه داشته باشید که به صورت مستقیم و دستی همه جا نباید اقدام به نابود سازی حافظه اخصاص یافته شده کنید. اجتناب از اتصال پیچیده گذشته از اینکه اتصالات پیچیده موجب کاهش پرفرمنس (کارایی) می‌شود استفاده از آن‌ها (برای مثال، زمانی که نیاز است ارزیابی جداگانه تحت کد‌های جاوا اسکریپت شود) به صورت پیچیده‌ای حافظه بیشتری را در هر دو پُشته ++C و JavaScript برای بهینه سازی محاسبات اختصاص می‌دهند. بنابراین نیاز نیست همیشه از اتصالات پیچیده و ارزیابی آن‌ها تحت JS استفاده کرد. اجتناب از تعریف چند نوع ضمنی یکسان اگر یک عنصر QML یک خصوصیت سفارشی تعریف شده در QML داشته باشد، آن نوع خاصی و ضمنی خود را میگیرد. این مورد در بخش بعدی بیشتر توضیح داده شده است. اگر چندین نوع ضمنی یکسان در یک جزء درون خطی تعریف شده باشند، موجب هدر‌رفتن بخشی از حافظه خواهند‌ شد. در این وضعیت معمولاً بهتر است که جزء را به‌صورت صریح تعریف کنیم که بعدا می‌تواند دوباره استفاده شود. تعریف یک اخاصیت سفارشی اغلب می تواند در بهینه سازی عملکرد سودمندی داشته باشد (برای مثال، برای کاهش تعداد پیوند‌هایی که مورد نیاز است یا دوباره ارزیابی می‌شوند)، یا می‌تواند ماژولار بودن و قابلیت نگهداری یک جزء را بهبود بخشد. در این موارد، استفاده از خواص سفارشی پیشنهاد می‌شود. با این حال، نوع جدید باید، اگر از بیش از یک بار استفاده می شود، به جزء خود (فایل .qml) به منظور حفظ حافظه، تقسیم شود. استفاده مجدد از اجزای موجود اگر شما در حال تعریف یک جزء جدید هستید، لازم است دوباره بررسی کنید که چنین جزئی در پلتفرم شما وجود دارد یا خیر. در غیر این صورت شما باید موتور QML را مجبور به ساخت و ذخیره سازی نوع داده برای یک نوع که نیاز است کنید که به عنوان یک نوع تکراری که از اجزای موجود می باشد بارگذاری می‌شود. به جای کتابخانه‌های اسکریپتی پراگما از انواع تک تک (singleton) استفاده کنید. اگر از یک اسکریپت کتابخانه pragma برلی ذخیره داده های نمونه استفاده می‌کنید، به جای استفاده از یک نوع تک تکی از QObject استفاده کنید. نتیجه آن در کارآیی بهتر تاثیر گذار خواهد بود و باعث می‌شود حافظه کوچکتری در پُشته‌ی جاوا اسکریپت مورد استفاده قرار گیرد. اختصاص دادن حافظه در یک برنامه QML استفاده از حافظه یک برنامه مبتنی بر QML ممکن است به دو بخش تقسیم شود: استفاده از پُشته سمت ++C و پشته‌ی سمت JavaScript می‌باشد. برخی از حافظه اختصاص داده شده در هر یک از اجزاء غیرقابل اجتناب خواهند بود. به عنوان آن که توسط موتور QML یا موتور جاوا اسکریپت اختصاص داده می‌شود. در حالی که بقیه آن وابسته به تصمیمات گرفته شده توسط توسعه دهنده اپلیکیشن می‌باشد. پشته در ++C شامل موارد زیر خواهد بود: سربار ثابت و اجتناب ناپذیر از موتور QML (پیاده سازی ساختارهای داده، اطلاعات زمینه و غیره). اطلاعات هر جزء کامپایل شده و نوع داده‌ها، شامل هر یک از انواع و فرا داده هایی می‌باشد، که توسط موتور QML بسته به نوع ماژول‌ها و کامپوننت‌هایی است که توسط اپلیکیشن بارگیری می‌شوند. هر شیء‌ای که در داده‌های سی++ (شامل مقادیرِ خاصیت) به همراه هر یک از عناصر متا آبجکت هستند که وابسته به کامپوننت‌ (اجزای) معرفی شده توسط اپلیکیشن می‌باشند. هر داده ای که به طور خاص توسط QML اختصاص داده می‌شود که شامل کتابخانه‌های وارد شده نیز هستند. پشته در JavaScript شامل موارد زیر خواهد بود: سربار ثابت و اجتناب ناپذیر از موتور QML (خودش انواع از قبل ساخته شده‌ی جاوا اسکریپتی را دارد). سر بار ثابت شده و اجتناب ناپذیر از ادغام جاوا اسکریپت (توابع سازنده برای انواع داده های بارگذاری شده، قالب‌های توابع، و غیره). اطلاعات مربوط به هر نوع و و دیگر انواع داخلی توسط موتور جاوا اسکریپت که در زمان اجرا تولید می‌شود. هر شیء ای که به عنوان داده جاوا اسکریپتی (خاصیت‌های نوع var ، توابع جاوا اسکریپتی و هندلرهای سیگنال و عبارات بهینه نشده). متغیرهای اختصاص داده شده در زمان ارزیابی عبارت‌ علاوه بر این، یک پشته جاوا اسکریپتی اختصاص یافته شده برای استفاده از نَخ اصلی و دیگری در پُشته جاوا اسکریپت برای استفاده در WorkerScript اختصاص یافته می‌شود. اگر یک اپلیکیشن از یک عنصر WorkerScript استفاده نکند متحمل به سربار گیری نخواهد شد. اندازه پشته در جاوا اسکریپت می‌تواند به چندین مگابایت برسد و بنابراین برنامه‌های نوشته شده برای دستگاه هایی که دارای محدودی حافظه هستند، ممکن است بهترین با اجتناب از عنصر WorkerScript باشد، با وجود سودمندی آن در مدل‌ها لیستی، که به صورت یکپارچه ذخیره می‌شوند باشد. توجه داشته باشید که هر دو موتور QML و JavaScript به طور خوکار مخازن خود را از نوع داده های ملاحظه شده تولید خواهند کرد. هر کامپوننت (جزء) توسط یک برنامه بارگذاری می‌شود که یک نوع متمایز (صریح) بوده و هر عنصر (به جای کامپوننت) ویژگی‌های سفارشی خود را در QML که به صورت ضمنی است تعریف می‌کند. مثال زیر را در نظر بگیرید: import QtQuick 2.3 Item { id: root Rectangle { id: r0 color: "red" } Rectangle { id: r1 color: "blue" width: 50 } Rectangle { id: r2 property int customProperty: 5 } Rectangle { id: r3 property string customProperty: "hello" } Rectangle { id: r4 property string customProperty: "hello" } } نمونه‌های قبلی مستطیل‌های r0 و r1 دارای ویژگی‌های اختصاصی (سفارشی) نبودند. بنابراین موتورهای جاوا اسکریپت و QML هر دو آنها را از همان نوع در نظر می‌گیرند. به عبارت دیگر هر دوی آن‌ها به عنوان یک نوع صریح از Rectangle (مستطیل) در نظر گرفته می‌شوند. مستطیل‌های r2، r3 و r4 هر یک از آنها با داشتن ویژگی‌های اختصاصی خود هر کدام با انواع مختلف ضِمنی در نظر گرفته شده‌اند. حتی اگر اطلاعات مربوط به ویژگی‌های یکسانی داشته باشند. ملاحظات اختصاصی در عُمق حافظه هرگاه تصمیماتی در خصوص تخصیص دادن حافظه یا کارآیی آن به وجود آید، این مهم است که تاثیرات شدید در عملکرد پردازنده مرکزی و مخازن مربوط به آن، صفحه بندی‌‌های سیستم‌عامل و بازیافت حافظه (GC) در جاوا اسکریپت را در نظر داشته باشید. بنابراین راه حل‌های بالقوه باید با دقت بررسی شوند تا مطمئن شوید که بهترین مورد را انتخاب کرده‌اید. هیچ مجموعه‌ای از دستور العمل‌های کلی نمی‌تواند یک درک جامع ای از اصول اساسی علوم رایانه را با یک دانش علمی از جزئیات پیاده سازی کرده و پلتفرمی که یک توسعه‌دهنده‌ی نرم‌افزار در حال توسعه آن است را جایگزین کند. تقسیم بندی تقسیم بندی به عنوان یک مسأله برای توسعه در سی‌پلاس‌پلاس است. اگر توسعه‌دهنده برنامه هیچ نوع یا پلاگینی از سی‌پلاس‌پلاس را تعریف نکند، ممکن است آنها را در این بخش با خیال راحت نادیده بگیرند. با گذشت زمان، یک برنامه بخش بزرگی از حافظه را برای خود اختصاص داده و داده‌ها را به آن حافظه ارسال می‌کند و بعضی اوقات برخی از قسمت های آن را پس از اتمام استفاده آن‌ها را آزاد می‌کند. این می‌تواند منجر به «حافظه آزاد» شده ای در تکه‌های غیر مجاور شود که نمی‌تواند برای برنامه های کاربردی دیگر توسط سیستم‌عامل مورد استفاده قرار گیرد. همچنین تاثیر شدیدی بر روی ذخیره سازی پنهان و دسترسی به ویژگی‌های یک اپلیکیشن می‌گذارد. زیرا داده‌هایی که زنده هستند (در حال استفاده) هستند، ممکن است در بسیاری از صفحات مختلف حافظه فیزیکی گسترش یابند. این به نوبه خود می‌تواند سیستم‌عامل را مجبور به (swap) یا مبادله کند که می‌تواند سبب شود تا عمل I/O ایجاد شود که به شدت عمل آهسته‌ای بشمار می‌آید. تقسیم بندی می‌تواند توسط دیگر موارد تخصیص دهنده حافظه، با کاهش میزان حافظه‌ای که در هر زمان با دقتِ مدیریتِ زمان زندگیِ اشیاء مورد بررسی قرار گیرد. با تمیز کردن و باز سازی دوره‌ای از حافظه‌ها یا با استفاده از زمان اجرا بر روی حافظه از تجزیه آن می‌توان جلوگیری کرد که توسط سیستم‌عامل بازیافت حافظه خودکار (GC) مانند جاوا اسکریپت ممکن است. بازیافت حافظه جاوا اسکریپت سیستم بازیافت حافظه به صورت خودکار را فراهم می‌کند. حافظه ای که در دسته جاوا اسکریپتی قرار دارد (بر خلاف پُشته در سی‌پلاس‌پلاس) متعلق به موتور خود جاوا اسکریپت است. این موتور به طور دوره‌ای تمامی داده‌های نا مشخص (غیر قابل استفاده) را در پُشته‌ی جاوا اسکریپت جمع آوری می‌کند. پیامد‌های بازیافت حافظه بازیافت حافظه، مزایا و معایبی دارد. این به این معنی است که مدیریت عمر مفید شیء به صورت دستی اهمیت کمتری دارد. با این حال، این بدان معنی است که یک عمل بالقوه طولانی مدت ممکن است توسط موتور جاوا اسکریپت در زمانیکه که خارج از کنترل توسعه‌دهنده نرم افزار است آغاز شود. استفاده از پُشته در جاوا اسکریپت با دقت بسیاری توسط توسعه‌دهنده برنامه مورد توجه قرار می گیرد. لذا تکرار و مدت زمان بازیابی حافظه ممکن است تاثیر منفی بر تجربه نرمافزار داشته باشد. فراخوانی بازیابی حافظه یک برنامه نوشته شده در QML (به احتمال زیاد) در یک مرحله‌ای به بازیافت حافظه (GC) نیاز دارد. در صورتی که حجم حافظه آزاد شده کم باشد سیستم بازیافت خودکار حافظه توسط موتور جاوا اسکریپت انجام می‌شود. بعضی اوقات این کار در صورتی که توسعه‌دهنده‌ی نرم‌افزار تصمیمی در مورد اینکه چه زمانی بازیافت حافظه را انجام دهند نگرفته باشد می‌تواند مناسب باشد. (اگر چه که معمولاً این مورد مطرح نمی‌شود). توسعه‌دهنده نرم‌افزار احتمالاً می‌داند که برنامه چه زمانی را به مدت قابل ملاحظه‌ای بیکار است. اگر یک برنامه QML از حافظه بسیار زیادی از پُشته جاوا اسکریپت را مصرف کند، باعث می‌شود چرخه و تکرار‌های مکرری در بازیافت حافظه صورت گیرد که در وظایف حساس بر روی کارآیی تاثیر خواهد گذاشت. مانند (لیست پیمایش، انیمیشن‌ها، و غیره). توسعه‌دهنده نرم‌افزار ممکن است به‌طور دستی به بازیافت حافظه در طول دوره صفر فعالیت کمک کند. دوره های بیکاری برای انجام بازیافت حافظه ایده آل هستند چرا‌‌که کاربر هیچ‌گونه تضعیفی را در تجربه کاربری خود (فریم‌های پرش شده، انیمیشن‌های پر‌تحرک و نا‌منظم و غیره) حس نخواهد‌کرد؛ که این تضعیف تجربه، در‌نتیجه فراخوانی بازیافت حافظه به هنگام فعالیت برنامه رخ می‌هد. توسعه‌دهنده نرم‌افزار احتمالاً می‌داند که برنامه چه زمانی را به مدت قابل ملاحظه‌ای برای بیکاری بکار می‌گیرد. اگر یک برنامه QML از حافظه بسیار زیادی از پُشته جاوا اسکریپت را مصرف کند، باعث می‌شود چرخه و تکرار‌های مکرری در بازیافت حافظه صورت گیرد که در وظایف حساس بر روی کارآیی تاثیر خواهد گذاشت. مانند (لیست پیمایش، انیمیشن‌ها، و غیره). از طرفی ممکن است توسعه‌دهنده با دستکاری بازیافت حافظه موجب تخریب آن شوند. بازیافت حافظه ممکن است به صورت دستی با فراخوانی gc() در جاوا اسکریپت اعمال شود. این باعث می‌شود یک چرخه بازیافت جامع از حافظه صورت بگیرد که ممکن است مدت زمانی بین چند صد تا بیش از هزار میلی ثانیه برای تکمیل آن سپری شود. بنابراین در صورت امکان باید از آن اجتناب شود. حافظه در مقابل کارآیی در بعضی موارد ممکن است که زمان پردازش حافظه برای سبک سنگین کردن افزایش مصرف حافظه کاهش یابد. برای مثال، ذخیره سازی نتیجه یک جستجوی نماد که در یک حلقه تنگ به یک متغیر موقت در یک عبارت جاوا اسکریپتی استفاده می‌شود که در بهبود ارزیابی این عبارت تاثیر قابل توجهی خواهد داشت. اما این شامل تخصیص یک متغیر موقت می‌باشد. در بعضی از موارد، این سبک سنگین کردن ممکن است معقول باشد (مانند موارد فوق، ک تقریباً همیشه منطقی هستند)، اما در موارد دیگر ممکن است بهتر باشد تا اجازه دهیم تا پردازش طول بکشد تا از افزایش فشار بر روی حافظه سیستم جلوگیری شود. در بعضی از موارد، تاثیر افزایش فشار بر روی حافظه می‌تواند شدید باشد. در برخی موارد، استفاده دوباره از حافظه ممکن است برای به دست آوردن عملکرد پیشنهادی منجر به افزایش ترافیک صفحات یا ترافیک بر روی حافظه نهان شود که باعث کاهش عملکرد شدید آن خواهد شد. این همیشه برای ارزیابی لازم است، تاثیر تعاملات با دقت به منظور تعیین اینکه بهترین راه حل در یک وضعیت خاص است مهم هستند.
  3. 8 امتیاز
    جزئیات در ++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 قابل اجرا هستند. اگر شما با نسخه های اخیر این کامپایلر ها کار می‌کنید می‌تواین بلافاصله با سی‌++ ۱۷ کار کرده و آن را تجربه کنید.
  4. 5 امتیاز
    سلام، کتابخانه‌های 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.
  5. 4 امتیاز
    دو هفته پیش، نشست ۲۰۱۸ سی‌پلاس‌پلاس آغاز شد. شرکت کننده‌ها مدال‌های خودشان را دریافت کردند چرا که همه‌ چیز با هماهنگی بسیار خوبی به پایان رسید. این رویداد به عنوان یکی از چندین رویداد مهمC++ بشمار می‌رود که هرساله توسط حامیان و علاقه‌مندانش برگزار می‌شود. سخنان کلیدی امسال در این رویداد سه یادداشت کلیدی وجود داشت، که با حضور Andrei Alexandrescu ،Lisa Lippincott و Nicolai Josuttis ارائه شد. اولین سخنران Andrei Alexandrescu بود، او این کنفرانس را با افکار و اندیشه‌های خودش برای شروع آغاز کرد عنوان موضوع آن به بَد بودنِ کپی constexpr از static if اشاره می‌کرد. او یک سخنران سرگرم کننده است، بنابراین سخنرانی او بسیار طبیعی در مورد یادداشت‌های خودش منعکس می‌شد. همچنین او به عنوان یک توسعه‌دهنده‌ی ++C به نقاط بسیاری اشاره کرد که کمیته‌ی استاندارد سازی زبان حرف‌های او را تایید می‌کرد. سخنران دوم Lisa Lippincott روز دوم را با یک سخنرانی آغاز کرد که دیدگاه‌های مختلفی در مورد محاسبات و منطق که در آن به کار می‌رود ارائه داد. زمانی که مُجری لیزا را دعوت کرد، می‌دانست که این موضوع به عنوان یک نکته مهم برای فکر کردن است، چیزی که همه نمی‌توانند به طور مستقیم آن را درک کنند و به آن دسترسی پیدا کنند. اما این تلنگری برای نحوه‌ی درک کُد از دیدگاه ریاضی و دیدگاه جدیدی بود. سخنران سوم و آخر Nicolai Josuttis بود که در مورد، او اولین سخنران در نشست Hartmut Kaiser در سال ۲۰۱۴ بود. در آن سخنرانی بسیار خوب درخشیده، بنابراین در قسمت سوم نقص‌هایی را با جزئیات در مورد نقاط خشن ++C نشان داد. این سخنرانی بسیار صادقانه بود! چرا که بسیاری از جزئیات خشن بودن سی‌پلاس‌پلاس را عنوان کرد که همگی با آن موافق بودند. مذاکرات، مسابقه و گفتگوها‌ی چند دقیقه‌ای جلسات و رویداد‌های مرتبط با ++C همیشه گفتگو‌های بسیار خوبی دارد، و با یک مسیر مشخص که هدفش آوردن سخنرانان جدید با دیدگاه‌های جدید است برگزار می‌شود. بازخورد سخنران‌ها در این رویداد خوب بود چرا که به برخی از نکات در مورد چگونگی بهبود‌ها اشاره کردند. تصویر بالا مربوط به Hana Dusíková است که می‌توان به آن بهترین نمره را داد. بهترین سخنرانی هم مربوط به Andrei Alexandrescu بود که دنبال شد. در اولین عصر این رویداد، امتحان (Quiz) برجسته‌ترین مورد رویداد در آن روز بود. سازماندهی آن توسط Diego و اسپانسری آن توسط Conan است که برای چند سال اخیر حمایت شده است. به نظر می‌رسید که سوالات امتحانی این سال سخت‌تر از سوالات رویداد قبلی در سال گذشته بوده است. اما بار دیگر این سرگرمی ترکیب بسیار خوبی با ترس از سی‌++ را داشت. هر یک از سوالات امتحانی یک خروجی از کُد را تولید می‌کردند، که هر گروه باید برای کسب امتیاز آن را می‌نوشتند. ده نوع کُد امتحانی وجود داشت که تهیه کننده‌ی گزارش برای به تصویر کشیدن آن‌ها زیاد خوب عمل نکرده است. بنابراین تصویر زیر مربوط به سوالات چالشی در مورد سی‌پلاس‌پلاس است: گفتگو‌های کوتاه در طی جلسات سی‌پلاس‌پلاس صورت می‌گیرد. اما در سال‌های گذشته روش به گونه‌ای تغییر یافته است که سوالات پرسیده شده باید با گفتگو‌ها و سوالات دیگر رقایبت می‌کردند. با این حال این یک قالب و روش جالبی بود که نتیجه‌ی موفقیت آمیزی را داشت. همه‌ی شرکت کننده‌ها می‌توانستند این سوالات رو ببینند و در مورد آن‌ها تفکر کرده و پاسخ دهند. نکته: شما می‌توانید تصاویر ویدیویی مربوط به این رویداد را از کانال رسمی آن در یوتیوب دریافت کنید.
  6. 4 امتیاز
    فایل‌ها/تغییرات پروژه را چطوری کنترل کنیم ؟ در وهلهٔ اوّل شاید بگید چه نیازیه ؟ خب برنامه رو می‌نویسیم و میریم دیگه !. درسته برنامه‌اتون را می‌نویسید و می‌روید؛ امّا به کجا چنین شتابان ؟ آیا همیشه برنامهٔ شما کوچک‌خواهد بود ؟ آیا قراره برنامهٔ شما در صد خط تمام بشه ؟ یا اینکه کلاً قصد توسعه‌اش رو دیگه ندارید ؟ خب شاید یکی دیگه داشت :). فرض کنید برنامهٔ‌تان را نوشتید : 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. 4 امتیاز
    سلام .امیدوارم اشتراک گذاری ها و اطلاع رسانی هاتون حالا حالا ها ادامه داشته باشه... خدا قوت
  8. 3 امتیاز
    اصطلاحاتی که بهتر است در مورد C++ مدرن بدانید! داشتم به این فکر می‌کردم که برخی از مبتدیان برنامه‌نویسی به خصوص کسانی که به سراغ زبان‌هایی مثل سی++ می‌روند معمولاً مستقیم وارد کد نویسی می‌شوند و به این گمان که آغاز برنامه‌نویسی یعنی نوشتن یک کد با خروجی «سلام، دنیا»! دریغ از آن‌ که بعضی از موارد مانند «معرفی کامپایلر و انواع آن» و حتی «ساختار برنامه‌های نوشته شده تحت سی‌پلاس‌پلاس» و یا حتی «مدیریت حافظه» را در نظر بگیرند! من معمولاً در مقالات و آموزش‌های خودم به این اشاره می‌کنم که قبل از هر چیز باید با ساختار برنامه‌های نوشته شده‌ی یک زبان آشنا شد و سپس به بررسی موارد دیگر مانند نحو زبان و یا دیگر ویژگی‌های آن. بنابراین، یکی از خطرناک‌ترین عواملی که موجب خونریزی داخلی یک نرم‌افزار در برنامه‌های نوشته شده توسط برنامه‌نویس درC++ می‌شود عدم مدیریت حافظه‌ی اختصاص یافته است که باید بعد از اختصاص یافتن حافظه در زمان معین آن را آزادسازی کند. در صورتی که این کار صورت نگیرد عمل Memory Leak (نَشتِ حافظه) رخ داده است. بسیاری از علاقه‌مندان بر این باورند که چون سی++ دارای GC یا همان Grabbage Collector (زباله‌روب) نیست که البته صحیح است! سی++ دارای GC نیست و این امر محدودیت یا نکته ضعف آن هم نیست! سی++ همه چیز را آزادانه در اختیار برنامه‌نویس قرار می‌دهد تا خود در زمان مناسب روش مدیریت حافظه را انتخاب کند. در علوم رایانه بازیافت حافظه یا زباله‌روبی نوعی مدیریت حافظه‌ی خودکار است که عمل مدیریت حافظه‌های اختصاص یافته شده را به دست می‌گیرد و اکثر زبان‌های برنامه‌نویسی مانند #C، جاوا و دیگر موارد مشابه به آن مجهز به این ویژگی هستند که البته وجود چنین ابزار‌هایی می‌تواند توهمی را ایجاد کند مبنی بر آن که دیگر نیازی به مدیریت منابع نیست، اما در بعضی موارد مدیریت منابع هنوز یک الزام است چرا که منابع آزاد شده هنوز هم دلیل بر نشتِ حافظه هستند. این نشت حافطه زمانی اتفاق می‌افتد که اشیاء هنوز قابل دسترس از طرف اشیاءای که زنده هستند اما هرگز مورد استفاده‌ی دوباره قرار نمی‌گیرند اتفاق بی‌افتد. در بسیاری از زبان‌های برنامه‌نویسی این ویژگی وجود دارد که طبیعتاً مدیریت توسط GC راه حل بسیار خوب و بی نقصی نیست. اما با توجه به عدم وجود GC در سی++ اکثراً با روش‌های دستی برای مدیریت حافظه می‌پردازند که رایج‌ترین روش آن استفاده از عمل new و delete در اختصاص دادن و آزاد‌سازی حافظه است. بسیاری از ما با سی++ در دانشگاه و یا دروس مرتبط با مفاهیم اولیه برنامه‌نویسی آشنا شده ایم، اما معمولاً مفاهیم مربوطه برای نسل‌های قبلی و منسوخ شده‌ی این زبان است. بهتر است در نظر داشته باشید که برنامه‌نویسی مدرن یعنی پیروی از اصول و قوانین جدیدی که در تکامل یافتن یک زبان به کار گرفته می‌شود. RAII : Resource Acquisition is initialization بنابراین، باید در نظر گرفت مدیریت حافظه از استاندارد ۱۱ به بعد این زبان به روش‌های بسیار مدرن‌تری هوشمند سازی شده است. یکی از بهترین تکنیک‌های موجود در هسته‌ی زبان الگوی RAII است. تکنیک RAII مخفف «Resource Acquisition is initialization» به مفهوم (کنترل تخصیص منابع و آزاد‌سازی آن‌ها) یک ویژگی اصلی در سی‌پلاس‌پلاس است، که متکی به کامپایلر (همگردان) برای فراخوانی خودکار بعضی موارد عنوان می‌شود. با قرار دادن چنین کدی در مخرب (ویرانگر) دیگر به فراخوانی آن کد توسط برنامه‌نویس نیست و کامپایلر خود این کار را انجام می‌دهد. به طور کلی این الگو هر شیء را مجبور می‌سازد تا در زمان مواجه با رفتار‌های ناهنجار خود را پاکسازی کند. به طور کلی هنگامی که شما یک شیء را مقدار‌دهی اولیه می‌کنید، قبل از انجام آن باید منابع مورد نیاز آن را تأمین کنید (در سازنده). هنگامی که یک شیء از محدوده‌ خارج می‌شود، هر منبعی را که مورد استفاده قرار داده است باید آزاد کند (در مخرب - ویرانگر). نکات کلیدی هرگز نباید یک شیء به حالت نیمه آماده یا نیمه از بین‌ رفته وجود داشته باشد! وقتی که یک شیء ساخته می‌شود، آن شیء باید در حالت آماده باش برای استفاده باشد. وقتی یک شیء از محدوده خارج می‌شود، باید منابع اختصاص یافته‌ی خود را در حافظه آزاد کند (کاربر مجبور به انجام کار دیگری نیست). آیا RAII عنوان بدی برای مفهوم این تکنیک است! از نظر خالق سی‌پلاس‌پلاس نام بهتر می‌تواند به صورت زیر باشد: مدیریت منابع مبتنی بر حوزه (محدوده یا دامنه) : Scope Based Resource Management چیزی که تکنیک RAII را نقض می‌کند چیست؟ اشاره‌گر‌های خام و تخصیص حافظه فراخوانی با کلمه‌ی کلیدی new برای دست آوردن یا اختصاص دادن منبع (حافظه). فراخوانی با کلمه‌ی کلیدی delete برای آزاد‌سازی منبع (حافظه). اما این مورد به صورت خودکار بعد از خروج از محدوده توسط اشاره‌گر‌ها صورت نمی‌گیرد. void rawPtrFn() { // acquire memory resourceNode* n = newNode; // manually release memory delete n; } بنابراین در صورتی که برنامه‌نویس استفاده از کلمه‌ی کلیدی delete را برای آزاد‌سازی حافظه فراموش کند (نشتِ حافظه) رخ می‌دهد. بنابراین این عمل کافی است تا تکنیک RAII را نقض کنیم. void UseRawPointer() { // Using a raw pointer -- not recommended. Song* pSong = new Song(L"Nothing on You", L"Kambiz Asadzadeh"); // Use pSong... // Don't forget to delete! delete pSong; } بنابراین، راه حل RAII برای این امر در چیست؟ کلاسی داشته باشید که : حافظه را هنگام مقدار‌دهی اولیه تخصیص دهد. حافظه را هنگام فراخوانی مخرب (ویرانگر) آزاد کند. دسترسی به اشاره‌گر‌های زیرین را امکان‌پذیر کند. اشاره‌گر‌های هوشمند (Smart Pointers) این ویژگی اساساً مدیریت حافظه‌ی خودکار را ارائه می‌دهد. زمانی که یک اشاره‌گر هوشمند دیگر استفاده نمی‌شود (زمانی که از محدوده‌ی خود خارج می‌شود) حافظه‌ی مورد نظر خود را به طور خودکار آزاد می‌کند.توجه داشته باشید که اشاره‌گر‌های سنتی با عنوان اشاره‌گر‌های خام (Raw Pointer) شناخته می‌شوند. اشاره‌گر‌های هوشمند را می‌تواند یک شکل کلی از GC در نظر گرفت؛ نوعی مدیریت خودکار وقتی که دیگر توسط برنامه مورد استفاده قرار نمی‌گیرند حافظه‌ی اختصاص یافته‌ی آن شیء به طور خودکار حذف می‌شود. در استاندارد ۱۱ سی‌پلاس‌پلاس سه نوع اشاره‌گر هوشمند معرفی شده است که همه‌ی آن‌ها در فایل سرآیند <memory> از کتابخانه‌ی استاندارد STL معرفی شده‌اند. کلاس std::unique_ptr یک اشاره‌گر هوشمند که دارای یک منبع تخصیص حافظه‌ی پویا است. کلاس std::shared_ptr شامل یک اشاره‌گری است که دارای یک منبع تخصیص حافظه‌ی پویا با تفاوت اینکه می‌تواند چندین شیء را به صورت اشتراکی از یک منبع مشترک ردیابی کند. کلاس std::weak_ptr مانند std::shared_ptr است که شمارنده‌ی آن افزایش نمی‌یابد. به مثال زیر توجه کنید: { std::unique_ptr<int> p(new int); // شیء p قابل استفاده در داخل حوزه است. } // در این بخش که خارج از دامنه‌ی اشاره‌گر است حافظه‌ی اختصاص یافته آزاد می‌شود. همانطور که مشخص است یک شیء که تحت اشاره‌گر هوشمند مورد استفاده قرار می‌گیرد تا زمانی که خارج از حوزه‌ی خود قرار نگیرد قابل استفاده خواهد بود. نمونه کد پایین مثالی از نحوه‌ی نمونه سازی تحت اشاره‌گر‌های هوشمند است. void UseSmartPointer() { // Declare a smart pointer on stack and pass it the raw pointer. unique_ptr<Song> song2(new Song(L"Nothing on You", L"Kambiz Asadzadeh")); // Use song2... wstring s = song2->duration_; //... } // song2 is deleted automatically here. این مقاله در یک فرصت مناسب به به جزئیاتی بیشتری خواهد پرداخت
  9. 3 امتیاز
    سلام، مدتی بود که نسخه‌ی ۲۰۱۷ کامپایلر MSVC با مشکل عدم بازگشت مقدار صحیح از نسخه‌ی استاندارد زبان مواجه بود. هرچند توسط توسعه دهندگان این مشکل به مایکروسافت گزارش داده شده بود اما ظاهراً مشکل همچنان پا برجاست. و اما مشکل، کد زیر را با کامپایلر MSVC2017 اجرا کنید: #include <iostream> int main() { std::cout << __cplusplus << std::endl; std::cin.get(); return 0; } اگر دقت کنید مقدار بازگشتی این کد 199711خواهد بود. در حالی که باید مقدار بازگشتی آن با توجه به استاندارد زبان باید یکی از دو مقدار 201402L یا 201403 باشد. برای حل این مورد من پچی را فراهم کردم که می‌تونید این مشکل رو حل کنید. علاوه بر این یک سری ماکرو نویسی جزئی انجام شده که با دقت بیشتر و مقدار بازگشتی از نام خود کامپایلر را برای شما ارائه دهد. طبق اصلاحیه کد دستوری آن به صورت زیر خواهد که تکه‌ای از کد‌های یکی از پروژه‌های من به نام Genesis بود که حالا اینجا به اشتراک گذاشتمش: #include <iostream> using namespace std; #include "macro.h" int main() { Macro mc; cout << "Hello World!" << endl; cout << "C++ Standard Version (MSVC) : " << __cplusplus << endl; cout << "C++ Standard Version (with bug fix in MSVC : " << __GENESIS_CPP_VALUE__ << endl; cout << "C++ Standard Version (with bug fix in MSVC using function: " << mc.get__cplusplus(false) << endl; cout << "Compiler name using flag true: " << mc.get__cplusplus(true) << endl; return 0; } خروجی: Hello World! C++ Standard Version (MSVC) : 199711 C++ Standard Version (with bug fix in MSVC : 201703 C++ Standard Version (with bug fix in MSVC using function: 201703 Compiler name using flag true: MSVC++ برای دریافت این اصلاحیه به مخزن آن مراجعه نمایید.
  10. 3 امتیاز
    طبق گزارش @قاسم رمضانی منش عزیز، مبنی بر اینکه بلوک کُد بر روی مرجع فاقد شماره گذاری می‌باشد در یک به‌روز رسانی از نسخه‌ی ۹.۱۰ به ۹.۱۳.۱ در پلاگین HilightJS این مورد حل گردید. نمونه خروجی‌: // 'Hello World!' program #include <iostream> int main() { std::cout << "Hello World!" << std::endl; return 0; }
  11. 3 امتیاز
    استفاده از توزیع‌های آماری برای تولید اعداد تصادفی سرعت رو پایین میاره. چون مشخهٔ آماری توزیع (مثلاً توی مورد شما توزیع یکسان) باید در هنگام تولید اعداد رعایت بشه. به همین خاطر موقع تولید عدد ممکنه سه یا چهار بار عدد تصادفی تولید بشه. این مسأله موقع استفاده از توزیع‌های آماری پیچیده‌تر مثل توزیع نرمال شدیدتر هم میشه. تولید عدد تصادفی با توزیع نرمال در سی‌پلاس‌پلاس خیلی کنده. در مقابل اگر مشخصهٔ آماری براتون مهم نیست می‌تونید از توابع سادهٔ rand استفاده کنید که از سخت‌افزار هم برای تولید عدد تصادفی کمک می‌گیرند: cat /dev/urandom | hexdump سرعت تولید عدد تصادفی به این روش خیلی بالاست اما آشفتگی لازم برای رمزنگاری رو نداره. اگه کاربرد خیلی جدی هست باید از randomبه‌جاش استفاده کنید که کندتره. توابع استاندارد هم از همین‌ها استفاده می‌کنند.
  12. 3 امتیاز
    این تاپیک مخصوص نمونه مثال‌هایی از پروژه‌ها، ماژول‌ها، کامپوننت‌ها و افزونه‌هایی می‌باشد که به صورت استاندارد و مدرن توسط سی‌پلاس‌پلاس و جاوا‌اسکریپت تحت فناوری Qt Quick طراحی می‌شوند. بدون هیچگونه تعارفی باید بگوییم هر نرم‌افزار (اپلیکیشن) و یا محصولی که ساخته می‌شود باید مطابق استاندارد‌هایی که مهندسین طراحی بر روی آن تاکید دارند توسعه یابد. این استاندارد‌ها ممکن است شامل اصول کُدنویسی، الگو‌های کد‌نویسی و یا قوانین روانشناسی رنگ‌ها، چیدمان اشیاء و هر موردی که می‌تواند در نتیجه‌ی آن تاثیر مثبت داشته باشد مهم هستند. با هدف اینکه می‌خواهیم نمونه مثال‌هایی را برای تازه واردین قرار دهیم تا بتوانند به درستی ونحوه‌ی صحیح طراحی و پیاده سازی دو بخش بک‌اِند و فرانت‌اِند آشنا شوند، نمونه مثال‌های شما باید دارای شرایط زیر باشند ساختار استاندارد کلاس‌ها و توابع در سمت سی‌پلاس‌پلاس استفاده از اشاره گر‌های هوشمند و یا رعایت اصول مدیریت حافظه استفاده از ماکرو‌های سفارشی iOS، Android، Windows، macOS و لینوکس جهت مدیریت بهتر بعضی از ویژگی‌های منحصربفرد سیستم‌عامل‌ها تا جایی که می‌توانید سعی کنید از ابزار‌های qmake و qbs در این نمونه مثال‌ها استفاده کنید رعایت رنگ‌بندی و همچنین اصول UX درست مهم است مثال شما باید برگرفته و بر اساس استاندارد‌های توصیه شده‌ی doc.qt.io باشد. استفاده‌ی بهینه از لنگر‌ها و همچنین ترازبندی صحیح لایه‌ها و نگه‌دارنده‌ها در سمت QML استفاده از تصاویر و یا قالب‌های استاندارد و سبک مانند SVG, PNG و base64 استفاده از فونت آیکونیک‌ها مانند (FontAwesome) این پست ممکن است ویرایش و یا به روز‌رسانی شود. در ادامه شما می‌توانید بر اساس شرایطی که ذکر شده است و با توجه به استاندارد‌های Google Material، Microsoft Fluent و همچنین Apple Human Interface نمونه مثال‌های خود را ارائه دهید که برخی از مثال‌های استاندارد در زیر آمده اند.
  13. 3 امتیاز
    در این پست قصد دارم به توضیح کد بخش VTK در مثال استفاده از VTK با Qt در گیتهاب بپردازم. از فایل VTKmain.h شروع میکنم : #include "qobject.h" #include "vtkCubeSource.h" #include "vtkOrientationMarkerWidget.h" #include <QVTKWidget.h> #include <vtkActor.h> #include <vtkAxesActor.h> #include <vtkDataSetSurfaceFilter.h> #include <vtkIdFilter.h> #include <vtkOBJReader.h> #include <vtkPLYReader.h> #include <vtkPolyDataMapper.h> #include <vtkProperty.h> #include <vtkRenderWindow.h> #include <vtkRenderer.h> #include <vtkSmartPointer.h> #include <vtkSphereSource.h> در بخش بالا کتابخانه های مورد نیاز در پروژه اینکلود شدند . یک سری از این کتابخانه‌ها مثل #include "qobject.h" کتابخانه‌های کیوت هستند. و یک سری کتابخانه‌ها که در اسم آن‌ها VTK به چشم می‌خورد کتابخانه‌های VTK هستند. برای اطلاعت بیشتر از هرکدام از این کتابخانه‌ها می‌توانید نام آن را جستجو کنید و به داکیومنت VTK برای آن کتابخانه و همچنین سورس آن کتابخانه دست یابید. بخش دوم کد : class VTKmain : public QVTKWidget { public: VTKmain(QWidget *parent = nullptr); ~VTKmain(); // vtk renderer vtkSmartPointer<vtkRenderer> renderer; vtkSmartPointer<vtkPolyDataMapper> mapper; vtkSmartPointer<vtkActor> actor; public slots: }; در این بخش یک کلاس به اسم VTKmain ساخته شده است و از QVTKWidget به ارث برده که یک کتابخانه توسعه داده شده توسط توسعه‌دهندگان VTK برای تعبیه کردن صفحه سه بعدی VTK در رابط کاربری گرافیکی Qt است. درون کلاس هم سه تعریف داریم که در فایل سورس بیشتر در مورد آنها توضیح خواهم داد. توضیحات فایل VTKmain.cpp : همانطور که مشاهده می کنید در این مثال ساده فقط در تابع سازنده کد نوشته شده است : VTKmain::VTKmain(QWidget *parent) : QVTKWidget(parent) در دو خط زیر پنجره را به اندازه مورد نظر ریسایز می‌کنیم و حداقل سایز پنجره را مشخص می‌کنیم : resize(1920, 1000); setMinimumSize(400, 200); در خط زیر با استفاده از vtkSmartPointer یک نمونه از vtkRenderer میسازیم و در اشاره‌گر هوشمند VTK که با اسم renderer در هدر تعریف کردیم میریزیم این روند کلی ایجاد یک شی با استفاده از اسمارت پوینتر VTK است و برای بقیه کلاس‌ها هم به همین منوال عمل می‌کنیم : renderer = vtkSmartPointer<vtkRenderer>::New(); در خط 19 رنگ پس‌زمینه را تنظیم می‌کنیم مقادیر ورودی این تابع بین 0 و 1 هستند و نشانگر استاندارد رنگی قرمز-سبز-آبی است : renderer->SetBackground(0, 0, 0); در خط 20 پنجره رندر از کلاس (به ارث برده شده از QVTKWidget ) را می‌گیریم و رندرر را به آن اضافه میکنیم : GetRenderWindow()->AddRenderer(renderer); در خط 22 و 23 یک مکعب ایجاد می‌کنیم : vtkSmartPointer<vtkCubeSource> cube = vtkSmartPointer<vtkCubeSource>::New(); در خط 26 یک نمونه برای mapper ایحاد می‌کنیم : mapper = vtkSmartPointer<vtkPolyDataMapper>::New(); در خط 27 پورت خروجی مکعب را به اتصال ورودی mapper می‌دهیم : mapper->SetInputConnection(cube->GetOutputPort()); در خطوط 29 تا 32 یک اکتور ایجاد کرده و mapper را که در بالا ساختیم برای ان تنظیم می‌کنیم و اندازه‌ی نقاط را برای ان تنظیم می‌کنیم و در خط 32 رنگ آن را تنظیم می‌کنیم : actor = vtkSmartPointer<vtkActor>::New(); actor->SetMapper(mapper); actor->GetProperty()->SetPointSize(1000); actor->GetProperty()->SetColor(1, 1, 0); //(R,G,B) در خط 33 این اکتور را به رندرر اضافه می‌کنیم : renderer->AddActor(actor); در خطوط 35 و 36 یک نمونه از axes برای نمایش جهت در کنار صفحه به صورت سه بعدی ایجاد می‌کنیم : vtkSmartPointer<vtkAxesActor> axes = vtkSmartPointer<vtkAxesActor>::New(); در خطوط 38 و 39 یک ویوپورت برای نمایش این جهت‌نما درست می‌کنیم چون نمیخواهیم با جابجایی سه بعدی صحنه این جهت‌نما از گوشه پنجره نکان بخورد و حرکت انتقالی در سه بعد داشته باشد: vtkSmartPointer<vtkOrientationMarkerWidget> axesViewPort = vtkOrientationMarkerWidget::New(); در خطوط 41 تا 46 به ترتیب رنگ دور ویوپورت ، اکتور جهت نمایش ، اینتراکتور رندر ویندو ، اندازه و مکان دو بعدی ویو پورت در صفحه ( بین پراپرتی‌ها بین 0 و 1 هستند ) ، فعال بودن و تعاملی بودن را تنظیم می‌کنیم که این گزینه آخر در صورت فعال بودن این امکان را به کاربر می‌دهد که با موس ویوپورت ساخته شده را جابجا کند : axesViewPort->SetOutlineColor(0.9300, 0.5700, 0.1300); axesViewPort->SetOrientationMarker(axes); axesViewPort->SetInteractor(GetRenderWindow()->GetInteractor()); axesViewPort->SetViewport(0.9, 0.0, 1.0, 0.1); axesViewPort->SetEnabled(1); axesViewPort->InteractiveOff(); در خط 48 مکان دوربین فعال را تنظیم می‌کنیم : renderer->GetActiveCamera()->SetPosition(1000, 2500, 1000); در خط 49 تابع رندر از رندرر را صدا زده : renderer->GetActiveCamera()->SetPosition(1000, 2500, 1000); و در خط 50 تابع ریست دوربین رندرر را صدا میزنیم تا تغییراتی که انجام دادیم قابل مشاهده شوند : renderer->ResetCamera();
  14. 3 امتیاز
    با توجه به وجود کتابخانه‌های متعدد در سی‌پلاس‌پلاس در این پُست قصد داریم آموزش‌هایی در رابطه با نحوه‌ی راه اندازی انواع کتابخانه‌ها را در سی‌پلاس‌پلاس توضیح دهیم. محیط‌های توسعه جهت نصب Visual Studio و Qt Creator خواهند بود. در صورتی که نیاز است کتابخانه‌ای را به صورت سفارشی کامپایل کنید نکاتی را باید مورد توجه قرار دهید که در ادامه آمده‌اند. قبل از هر چیز نیاز است توضیحاتی در رابطه با انواع کتابخانه‌ها داده شود. کتابخانه‌ها برای اینکه در پروژه مورد استفاده قرار بگیرند نیاز است آن‌ها از سمت منبع خود کامپایل و ساخته شوند. البته در این فرآیند باید توجه داشته باشید که نوع معماری در پیکربندی یک کتابخانه بسیار مهم است. برای مثال اگر قرار است کتابخانه‌ای را بر روی یک پروژه‌ای که تحت معماری x64 پیکربندی شده است و در وضعیت release منتشر شود، در این صورت حتماً باید کتابخانه مورد نظر تحت همین پیکربندی کامپایل شود. کتابخانه‌ها ممکن است خودشان وابسته‌ی کتابخانه‌های دیگری باشند. برای مثال بخشی از ماژول کتابخانه Boost و Poco وابسته‌ی کتابخانه‌ی OpenSSL می‌باشد. و یا بخشی از کتابخانه‌ی MySQL وابسته‌ی کتابخانه‌ی Boost می‌باشد. بنابراین قبل از پیکربندی پروژه تحت هر کتابخانه‌ای مطمئن شوید که پیش نیازات آن را در اختیار داشته باشید. توجه داشته باشید که حتماً راهنمای کتابخانه‌ی مورد نظر خود را جهت نحوه‌ی پیکربندی مطالعه نمایید، زیرا هیچ روش عامیانه‌ای وجود ندارد که بر روی تمامی کتابخانه‌ها صادق باشد. با توجه به نکات بالا آموزش لازم جهت پیکربندی و راه اندازی کتابخانه‌ها را تحت دو گزینه‌ی Boost و MFSL ادامه می‌دهیم: نسخه‌ی مورد نظر کتابخانه‌ی مورد نظر را از این بخش دریافت کنید. فایل‌ دریافت شده را استخراج و در یک مسیر مشخصی مانند C://کتابخانه‌ی شماکپی کنید. محیط کنسول در سیستم عامل را باز کنید، پیشنهاد می‌شود از Visual Studio Cross Tools Command Prompt استفاده کنید. به مسیر کتابخانه تحت دستور cd رفته و وارد آن شوید. در این مرحله نیاز است تا قبل از ساخت کتابخانه آن را پیکربندی کنید، بنابراین دستور زیر را اجرا خواهیم کرد: ./configure دقت کنید که این مرحله معمولاً در کتابخانه‌ها متفاوت می‌باشد، برای مثال در کتابخانه‌ی Boost فایلی به نام bootstrap.bat متخص ویندوز و فایل bootstrap.sh برای محیط‌های یونیکس موجود است که وظیفه‌ی پیکربندی و تولید فایل ساخت را بر عهده دارد. البته در نظر داشته باشید که این چنین پیکربندی در کتابخانه‌های خاص ممکن است و در بیشتر آن‌ها باید با دستورات configure و فلگ‌های موجود در هر یک از آن‌ها اقدام به پیکربندی کنید. بنابراین با توجه این مورد می‌توانید آموزش لازم را در این بخش پیگیری نمایید. بعد از پیکربندی دستور make، nmake، cmake و یا qmake متناسب با نوع ابزار سازنده باید اجرا شود تا کتابخانه بر اساس پیکربندی تنظیم شده شروع به کامپایل و ساخت کند. این مرحله معمولاً بر اساس قدرت پردازشی سیستم شما زمان متغیری خواهد داشت. بعد از به اتمام رسیدن زمان کامپایل کتابخانه‌ی مورد نظر فایل‌های lib را تحت پسوند‌های .dll در ویندوز و .lib و .so در لینوکس و یونیکس تولید خواهد کرد که بهتر است مسیر include برای هدر‌های کتابخانه و lib برای فایل‌های کامپایل شده مشخص شود. طبق شرایط ذکر شده برای مثال ما از کتابخانه‌ی SDL در این بخش استفاده خواهیم کرد. نسخه‌ی از پیش کامپایل شده مربوط به آن را از این بخش دریافت و استخراج نمایید. قست اول (نصب و راه اندازی تحت محیط Visual Studio) وارد محیط ویژوال استودیو شده و بعد از ایجاد پروژه بر روی پروژه راست کلیک و گزینه‌ی Properties را انتخاب کنید، به زبانه C/C++ رفته و زبانه General گزینه‌ی Additional Include Directories را انتخاب کنید. در ادامه مسیر include را از کتابخانه‌ی SDL به پروژه معرفی کنید. مرحله کنونی را تایید کنید، و به زبانه‌ی Linker و سپس General بروید، در این بخش گزینه‌ی Additional Linker Library را انتخاب و مسیر Lib را از کتابخانه‌ی SDL معرفی کنید. در این مرحله فایل‌های کتابخانه معرفی شده‌اند. به زبانه‌ی General و Input برگشته و در بخش Additional Dependences فایل‌های SDL2.lib و SDL2_image.lib و SDL2main.lib و SDL2_ttf.lib را معرفی کنید. در نهایت فایل‌های dll موجود در پوشه‌ی lib کتابخانه را در کنار فایل اجرایی کپی کنید. قست دوم (نصب و راه اندازی تحت محیط Qt Creator) پروژه خود را ایجاد و بر روی نام پروژه راست کلیک کنید. گزینه‌ی Add Library و سپس External Library را بزنید. طبق شرایط قبل گزینه‌های lib را همراه با include به پروژه‌ی خود اضافه نمایید. کد مربوط به فایل .pro به صورت زیر خواهد بود win32: LIBS += -L$$PWD/../../../../SDL2-2.0.8/lib/x86/ -lSDL2 -lSDL2main -lSDL2_ttf INCLUDEPATH += $$PWD/../../../../SDL2-2.0.8/lib/x86 DEPENDPATH += $$PWD/../../../../SDL2-2.0.8/lib/x86 طبق روش کامپایل برنامه را کامپایل کنید و قبل از اجرا فایل‌های dll یا lib را نسبت به پلتفرم خود در کنار فایل اجرایی پروژه قرار دهید. روش فوق در بیشتر کتابخانه‌ها قابل انجام است.
  15. 2 امتیاز
    سلام. عدم دسترسی به یک سیستم مناسب و با خبر نبودن از حساب کاربری گیت هاب خود یکی از مشکلاتی بود که در این چند ساله برنامه نویسان با آن روبرو بودند. چک کردن حساب ایمیل در تلفن همراه می توانست تا حدودی به این موضوع کمک کند. اما یک اپلیکیشن اختصاصی برای این مورد می تواند این امر را به بهترین شکل پوشش دهد. بعد از کارهایی که برروی اپلیکیشن رسمی شرکت گیت هاب برای پلتفرم iOS انجام شد و خوشبختانه بدون هیچ مشکلی در بزرگ رویداد و کنفرانس شرکت و مایکروسافت - GitHub Universe 2019 در تاریخ November 13-14, 2019 رونمایی شد. به عنوان یکی از اعضای شرکت این نوید را می دهم که نوبت به آن رسید تا اپلیکیشن برای اندروید نیز پیاده شود. در حال حاضر این اپلیکیشن در حال توسعه است و هنوز رونمایی نشده است. برای این اپلیکیشن میزان پشتیبانی API 21+ Android device در نظر گرفته شده است و خواهد توانست از نسخه Android 5.0 به بالا را پشتیبانی کند. می توانید پیشنهادات و نظرات خود را نیز ایمیل کنید. Max [@] Asrez {.DOR.} com Hi, I'm Max Base. GitHub team did work on the official GitHub application for the iOS platform and fortunately unveiled at the big event and conference(GitHub Universe 2019 on November 13-14, 2019). As a member of the company, I have the promise that the app will launch for Android. This app is currently under development and has not been unveiled yet. This app is designed to support Android 21+ API and will support Android 5.0 or later. You can also email your suggestions and comments. Max [@] Asrez {.DOR.} com Best, Max Base با تشکر Max Base / مکس بیس
  16. 2 امتیاز
    آموزش زبان برنامه‌نویسی سوئیفت - جلسه چهارم مواردی که در این جلسه یاد خواهید گرفت: کامنت‌ها، دو ویژگی نوع‌های داده Int، آپِریِتر‌ها ( Operators )، کلمات کلیدی break,continue,fallthrough عرض ادب و احترام دارم خدمت شما عزیزان. در این جلسه به مواردی که در بالا گفته شده، پرداخته میشه، پس تا انتها با من همراه باشید و امیدوارم از این جلسه هم نهایت استفاده رو بکنید. کامنت‌ها در زبان برنامه‌نویسی سوئیفت مثل اکثر زبان‌های برنامه‌نویسی دیگه، از کامنت‌ها استفادهای زیادی میشه و معمولا برای هدفی کامنت‌ها نوشته میشن و می‌تونن شامل موارد زیر باشن: برای غیر‌فعال کردن موقتی یک قسمت/بخش از کُد برای توضیح دادن یک قسمت/بخش از کُد برای دیگر برنامه‌نویسان/توسعه‌دهندگان یا توضیح کُد برای اینکه در مراجعات بعدی به کُدهای پروژه کارکرد هر قسمت از برنامه را متوجه شوید انواع کامنت‌ها در زبان برنامه‌‌نویسی سوئیفت: تَک خطی (‌ single-line ) چند خطی ( multi-line ) در کامنت تَک خطی، با گذاشتن دو علامت // می‌توانید توضیحات خود را فقط در همان خط درمورد کُد مورد نظر بنویسید و بعد از آن کامنت شما در واقع در حال تعریف دستور جدید هستید! به این مثال دقت کنید: let _web_site_name : String = "www.iostream.ir" print(_web_site_name) // Ouput the string of www.iostream.ir در مثال بالا همان‌طور که مشاهده می‌کنید از کامنت تَک خطی استفاده کردیم، و بعد از این کامنت شما نمی‌توانید انتظار داشته باشید که در خط جدید دوباره حالت کامنت‌گذاری برای شما همچنان فعال باشد! چرا که در بعدی دستوری جدید داریم نه کامنتی ادامه‌ی کامنت قبل!. و اما کامنت چند خطی که اسمش هم روشه، تا هر چند‌تا خط تا موقعی که آخر کامنت رو نَبسته باشید، می‌تونید توضیحات بنویسید. به مثال زیر دقت کنید: let _web_site_name : String = "www.iostream.ir" /* We can also use from of print("String.. and \(_web_site_name)") */ print(_web_site_name) در مثال بالا که مشاهده می‌کنید، شما هیچ محدودیتی برای اضافه کردن توضیحات بیشتر برای کُد‌های خود ندارید و تا هر چند خط توضیحات می‌توانید استفاده کنید. دو ویژگی نوع داده‌ی Int اگر بخواهیم کوچک‌ترین و بزرگ‌ترین عدد موجود از نوع داده‌ی Int را بدست آوریم، از دو ویژگی max,min استفاده می‌کنیم. هر دو ویژگی به همراه مثال در زیر آورده شده‌اند: print("Max => \(Int.max)") print("Min => \(Int.min)") با اجرای کد‌های بالا،‌ بزرگترین مقدار موجود نوع داده‌ی Int و همچنین کوچک‌ترین آن به شما نمایش داده می‌شود. آپِریِتر‌ها (‌ Operators ) آپِریِتر‌ها در هر زبان برنامه‌نویسی به عنوان پایه و اساس محاسبات و در بعضی موارد برای کارهای دیگر استفاده می‌شود. محاسباتی مانند، جمع، تفریق،تقسیم،ضرب و باقی مانده. در سوئیفت این 8 دسته آپرِیِتر وجود دارد: آپِریِتر‌ اِنتسابی ( Assigment Operator ) آپرِیِتر‌های محاسباتی ( Arithmetic Oprerator ) آپِریِتر‌ باقی‌مانده ( Remainder Operator ) آپِریِتر‌های مُرکب ( Compound Assigment Operator ) آپِریِتر‌های مقایسه‌ای ( Comparison Operators ) آپِریِتر ترِنِری ( Ternary Conditonal Operators ) آپِریِتر دامِنه ( Range Operator ) آپِریِتر‌های منطقی ( Logical Operators ) این‌ها، کل آپِریِتر‌ها در سوئیفت هستند که هر کدام عمل مخصوص به خود را انجام می‌دهند. در مثال‌های زیر هر کدام از این آپِریِتر‌ها را به همراه مثال برای شما آورده‌ایم. آپِریِتر‌های اِنتسابی ( Assigment Operator ) در کل اینکه ما یک مقداری را به متغییری انتساب می‌دهیم، عملا داریم از آپِریِتر‌‌ انتساب ( = ) استفاده می‌کنیم. چرا که کُدنویسی از سمت چپ شروع می‌شود و کاملا منطقی هم هست که مقدار دهی به یک متغییر از سمت چپ صورت می‌گیرد که مقدار سمت راست قرار می‌گیرد، با استفاده از آپِریِتر انتساب (‌ = ) در مابین مقدار و نام و نوع متغییر قرار می‌گیرد و عمل انتساب مقدار به متغییر را انجام می‌دهد. به مثال زیر دقت کنید: let _web_site_name : String = "www.iostream.ir" // Assigment Operator ( = ) print("Type of \(type(of : _web_site_name ) and the website of name \(_web_site_name)") در مثال بالا مشاهده می‌کنید که با استفاده از آپِریَتر انتساب (‌ = )‌، مقدار www.iostream.ir که یک مقدار رشته‌ای/متنی است را در متغیر web_site_name ذخیره می‌کنیم. در خط بعد هم نوع متغییر و مقدار موجود در متغییر web_site_name ذخیره نمایش می‌دهیم. آپرِیِتر‌های محاسباتی ( Arithmetic Oprerator ) در قسمت‌هایی از پروژه شده که ما باید محاسباتی رو انجام بدیم. این کار با استفاده از آپِریِتر‌های محاسباتی ( Arithmetic Operator ) صورت می‌گیرند. این آپِریِتر‌ها شامل: +،-،/،* و ٪ است. برای هر کدام از این آپریِترها، مثال‌هایی در زیر آورده شده است: آپِریِتر + var number_one, number_two : Int8 number_one = 50 number_two = 50 print("Result = > ", number_one + number_two) // Output the number 100 در مثال بالا همان طور که مشاهده می‌کنید، ابتدا دوم متغییر با نوع Int8 تعریف کرده و سپس در خطوط بعد به آن‌ها مقادیر ۵۰ را داده‌ایم. در نهایت عمل جمع ( + ) را روی آن‌ها به صورت مستقیم انجام و نمایش می‌دهیم. یا ساده‌تر بخواهیم تعریف کنیم، آپِریِتر ( + ) عمل جمع کردن اعداد و یا متصل کردن دو رشته را بر عهده دارد: var web_site_name, platform_name : String web_site_name = " www.iostream.ir :)" platform_name = " www.fanoox.com ;)" print("Binding two string => ", web_site_name + platform_name) // Ouput the website name and platform name => www.iostream.ir :) www.fanoox.com ;) و به همین راحتی می‌توانید تا بی‌نهایت عمل جمع و متصل کردن رشته‌ها را انجام دهید. تنها نکته‌ای که باید توجه داشته باشید این است که سوئیفت در برخورد با اعداد و این آپِریَتر، آن عبارت را محاسباتی در نظر می‌گیرد و در برخورد دو تا چند رشته، آن عبارت را عمل متصل کردن و الحالق ( concatentation ) در نظر می‌گیرد. آپِریِتر - این آپِریِتر برای کم کردن دو مقدار عددی از هم استفاده می‌شود، همان‌طور که در مثال زیر مشاهده می‌کنید: var number_one, number_two : Int8 number_one = 80 number_two = 30 print("Result => ", number_one - number_two) // Output the number 50 مثال بالا به خوبی نشان می‌دهد که دو مقدار ۸۰ و ۳۰ از هم کم شده با استفاده از آپِریِتر ( - ) و در نتیجه، خروجی برابر ۵۰ خواهد بود. آپِریِتر * این آپِریِتر عمل ضرب کردن دو عدد را انجام می‌دهد، به مثال زیر دقت کنید: var number_one, number_two : Int8 number_one = 50 number_two = 20 print("Result => ", number_one * number_two) // Output the number 1000 آپِریِتر ( *‌ ) عمل ضَرب را انجام می‌دهد و نباید به حرف ( x ) که شبیه به ضرب در ریاضیات است اشتباه گرفته شود. آپِریِتر / این آپِریِتر عمل تقسیم کردن دو عدد را انجام می‌دهد. به مثال زیر دقت کنید: var number_one, number_two : Double number_one = 50.0 number_two = 20.0 print("Result => ", number_one / number_two) // Output the number 2.5 در محاسباتی که عمل تقسیم را انجام می‌دهیم باید به این نکته دقت کنیم که اگر پروژه‌ی ما عملا برای محاسبات کار خاصی است باید از نوع داده‌ی Double یا Float استفاده کنیم که البته در محاسبات معمولی، نوع داده‌ی Float جواب‌گوی نیاز ما هم هست، اما در محاسباتی که نیاز به دقت بالایی دارند باید از نوع داده‌ی Double استفاده کنیم. آپِریِتر ٪ این آپِریِتر را با علامت درصد که شبیه همین است اشتباه نگیرید! چرا که در دنیای واقعی ما، علامت درصد برای نشان دادن مقداری از چیزی در یک محصول یا خدمات است ولی در دنیای کامپیوتر و برنامه‌نویسی این علامت که آپِریِتر خوانده می‌شود، به معنای باقی مانده‌ی بین دو عدد است که بعد از تقسیم‌های پی‌درپی که صورت می‌گیرد، بدست می‌آید. این باقی مانده یا ۰ است یا ۱. حتما شما هم عاشق صفر و یکی هستید که اساس کار کامپیوتر و سیستم شما را تشکیل می‌دهد!. یک مثال در زیر ببینید تا متوجه کارکرد این آپِریِتر شوید: var number_one, number_two : Int8 number_one = 10 number_two = 2 print("Result => ", number_one % number_two) // Output the number 0 اگر جزئی بخواهیم وارد شویم به این صورت است که ابتدا عدد ۱۰ بر ۲ تقسیم ( آپِریِتر / ) شده و سپس حاصلی که بدست می‌آید ۵ است و سپس همین ۵ دوباره تقسیم بر ۲ شده و ۲ بدست می‌آید و در اینجا ۲ ٪ ۲ می‌شود ۰. آپِریِتر باقی‌مانده ( Reminder Operator ) این آپِریِتر همان‌طور که در بالا و در قسمت آپِریِتر‌های محاسباتی قرار گرفته بود، کار محاسبه‌ی باقی‌مانده‌ی دو عدد را انجام می‌دهد: var number_one, number_two : Int8 number_one = 10 number_two = 2 print("Result => ", number_one % number_two) // Output the number 0 آپِریِتر‌های مُرکب ( Compound Assigment Operator ) که شامل عبارت‌های کوتاه‌شده یا به اصطلاح میانبُری برای عمل انتساب و محاسبه را فراهم می‌کند که شامل: =+،=-،=*،=/،=٪ است. در زیر توضیح مختصر به همراه یک مثال آورده شده است. آپِریِتر =+ در این حالت ما هم عمل انتساب را داریم و هم عمل جمع، با یک تیر دو نشان! به مثال زیر دقت کنید. var number_one : Int8 = 50 number_one += 50 print("Result =>", number_one) // Output the number 100 در خط دوم که ما با آن کار داریم، متغییر number_one به علاوه‌ی مقدار قبلی خودش، مقدار ۵۰ را هم به آن اضافه کرده و در نهایت در خود متغییر number_one ذخیره و نتیجه ۱۰۰ نمایش داده می‌شود. که بدون استفاده از میانبُر، به این شکل بود: var number_one : Int8 = 50 number_one = number_one + 50 print("Result =>", number_one) // Output the number 100 حتی می‌توانیم برای اتصال یک رشته به رشته‌ی دیگر استفاده کنیم: var web_site_name : String = "www.iostream.ir :) " web_site_name += " www.fanoox.com ;) " print("Result =>" web_site_name) // Ouput the web site of name => www.iostream.ir :) www.fanoox.com ;) که بدون استفاده از میانبُر به این شکل نوشته می‌شُد: var _web_site_name_and_platform_name : String = "www.iostream.ir :) " + " www.fanoox.com ;) " print("Result => ", _web_site_name_and_platform_name) // Ouput the web site of name and platform name => www.iostream.ir :) www.fanoox.com ;) آپِریِتر =- عمل تفریف و انتساب آن به مقدار فعلی متغییر را انجام می‌دهد. به مثال زیر دقت کنید:‌ var number : Int8 = 80 number -= 30 print("Result =>", number) // Ouput the number 50 مقدار ۸۰ که مقدار فعلی متغییر number است، از مقدار ۳۰ که در سمت راست متغییر قرار دارد، کم می‌شود و در نهایت مقدار ۵۰ در همان متغییر یعنی ‌number ذخیره می‌شود. آپِریِتر =* عمل ضرب و انتساب مقدار سمت راستی در مقدار فعلی متغییر سمت چپ را انجام می‌دهد. به مثال زیر دقت کنید: var number : Int8 = 80 number *= 30 print("Result =>", number) // Ouput the number 2400 آپِریِتر =/ مشابه آپِریِتر تقسیم ( / )‌، عمل تقسیم و انتساب آن به متغییر سمت چپ انجام می‌دهد. به مثال زیر دقت کنید: var number : Int8 = 10 number /= 2 print("Result =>", number) // Ouput the number 5 آپِریِتر =٪ عمل باقی‌مانده‌ی دو عدد که شامل مقدار فعلی متغییر سمت چپ و مقدار سمت را است را محاسبه کرده و در متغییر سمت چپ ذخیره می‌کند. به مثال زیر دقت کنید: var number : Int8 = 10 number ٪= 5 print("Result =>", number) // Ouput the number 0 آپِریِتر‌های مقایسه‌ای ( Comparison Operators ) برای مقایسه‌ی بین دو مقدار و در نتیجه برگرداندن مقدار True یا False مورد استفاده قرار می‌گیرند. بیشترین استفاده‌ی آنها در شرط‌هاست، اما می‌توان به صورت مستقیم هم از آن‌‌ها هم استفاده کرد. این آپِریِتر‌ها شامل ==،=!،>،<،=<،=>،==!،=== است. برای هر کدام مثالی در زیر آورده شده است. آپِریِتر == برای مقایسه دو مقدار استفاده می‌کنیم که مقدار در صورتی که مقادیر دو طرف مساوی باشند، مقدار True و در غیر اینصورت مقدار False برگشت داده می‌شود. به مثال زیر دقت کنید: print("True and False => ", 2 == 2) // Output the true print("True and False => ", 2 == 3) // Output the false در مثال بالا چونکه ۲ با ۲ برابر است،‌ مقدار نمایش داده شده،‌ true است. و در خط بعدی چونکه مقدار ۲ برابر با ۳ نیست،‌ مقدار false نمایش داده می‌شود.همچنین می‌توانید در شرط‌ها و حلقه‌ها هم استفاده کنید: if 2 == 2 { print("Ok!") }else { print("NO!") } // Ouput the string Ok! آپِریِتر =! اگر مقدار برابر با مقدار مقابل خودش نبود، نتیجه true و در غیر اینصورت نتیجه false خواهد بود. علامت ! ( نَقیض )‌ دقیقا معنا و مفهوم ( == ) را عوض می‌کند. به مثال زیر دقت کنید: print("True and False => ", 2 != 3) // Output the true print("True and False => ", 2 != 2) // Output the false در مثال بالا، ۲ مساوی ۳ نیست و این درست است. در خط بعدی ۲ مساوی با ۲ نیست که این درست نیست و مساوی هستش،‌ پس نتیجه false است. آپِریِتر > اگر مقدار سمت چپ کوچکتر از مقدار سمت راست بود، نتیجه true و در غیر اینصورت نتیجه false است. به مثال زیر دقت کنید:‌ print("The operator ( < ) => ", 1 < 2) // Output the boolean true print("The operator ( < ) => ", 2 < 1) // Output the boolean false آپِریِتر <‌ اگر مقدار سمت چپ بزرگتر از مقدار سمت راست بود، نتیجه true و در غیر اینصورت نتیجه false است. به مثال زیر دقت کنید: print("The operator ( > ) => ", 1 > 2) // Output the boolean false print("The operator ( > ) => ", 2 > 1) // Output the boolean true آپِریِتر =< اگر مقدار سمت چپ، بزرگتر یا مساوی مقدار سمت راست بود، نتیجه true است، در غیر اینصورت، نتیجه false خواهد بود. به مثال زیر دقت کنید: print("The operator ( >= ) => ", 1 >= 1) // Output the boolean true print("The operator ( >= ) => ", 1 >= 2) // Output the boolean false آپِریِتر => اگر مقدار سمت چپ، کوچکتر یا مساوی مقدار سمت راست بود، نتیجه true است، در غیر اینصورت، نتیجه false خواهد بود. به مثال زیر دقت کنید: print("The operator ( <= ) => ", 1 <= 2) // Output the boolean false print("The operator ( <= ) => ", 1 <= 1) // Output the boolean true نکته:‌ اولویت آپِریِتر مساوی (‌ = ) در این مورد بالا‌تر از آپِریِتر < یا > است و بنابر این در حلقه‌ها اگر این این آپِریِتر باشد، مبنا بر همین آپِریِتر قرار می‌گیرد. آپِریِتر === این آپِریِتر برای مقایسه‌ی دو شئ ( object ) استفاده می‌شود. اگر هر دو شئ از یک مرجع ( refrence ) بودند یا به عبارت دیگر شئ‌های ساخته شده در حافظه‌ی به نام Heap با تمام ویژگی‌ها و متغییر‌‌ها نگه‌داری می‌شوند و اگر شئ دیگر از همان حافظه‌ای که یک شئ دیگر استفاده می‌کند باشد، این آپِریِتر درمورد آن دو شئ نتیجه‌ی true و در غیر اینصورت نتیجه‌ی false را نمایش می‌دهد. در مبحث شئ‌گرایی بیشتر در این مورد صحبت ‌می‌کنیم،‌ در حال حاضر فقط همین را بدانید، کافیست. به مثال زیر دقت کنید: class WebsitePlatform { var web_site_name, platform_name : String init(web_name : String, platform : String) { self.web_site_name = web_name self.platform_name = platform } } let _web_site_and_plat_form_one = WebSitePlatform(web_name : "www.iostream.ir", platform : "www.fanoox.com") let _web_site_and_plat_form_two = WebSitePlatform(web_name : "www.iostream.ir", platform : "www.fanoox.com") if _web_site_and_plat_form_one === _web_site_and_plat_form_two { print("This is refrence!") }else { print("No,this is not refrence!") } اگر این کُد را خروجی بگیرید، به شما پیغام NO, this is not refrence را می‌دهد. چرا که هر دو شئ به صورت جداگانه در حافظه‌ی Heap ذخیره شده‌اند و هیچ‌کدام به دیگر کاری ندارد و به اصطلاح با هم ارتباطی ندارند و هر یک کَشک خودش را می‌سابد. و اگر ما بیایم به این شکل عمل کنیم، آنوقت دیگر یک ارجاع داریم به شئ‌ای مشخص: class WebsitePlatform { var web_site_name, platform_name : String init(web_name : String, platform : String) { self.web_site_name = web_name self.platform_name = platform } } let _web_site_and_plat_form_one = WebSitePlatform(web_name : "www.iostream.ir", platform : "www.fanoox.com") let _web_site_and_plat_form_two = _web_site_and_plat_form_one if _web_site_and_plat_form_one === _web_site_and_plat_form_two { print("This is refrence!") }else { print("No,this is not refrence!") } به جای ایجاد شئ جدید، همان شئ اول را به متغییر دوم انتساب می‌دهیم که الان در واقع دو شئ داریم که شئ دومی به شئ اولی اشاره می‌کند و حالا باید این دو شئ، دوتایی کَشک بسابن! یعنی اینکه الان دوتایی به هم در ارتباطن و شئ دومی ارجاعیست به شئ اول. نتیجه خروجی هم This is refrence است. آپِریِتر ==! اگر دو شئ از یک مرجع نبودند، و هر کدام در حافظه‌ای جداگانه نگه‌داری می‌شدند، نتیجه به صورت true و اگر دو شئ‌ از یک مرجع بودند، نتیجه false خواهد بود. کاملا برعکس آپِریِتر (‌ ===‌ ). به مثال زیر دقت کنید: class WebsitePlatform { var web_site_name, platform_name : String init(web_name : String, platform : String) { self.web_site_name = web_name self.platform_name = platform } } let _web_site_and_plat_form_one = WebSitePlatform(web_name : "www.iostream.ir", platform : "www.fanoox.com") let _web_site_and_plat_form_two = WebSitePlatform(web_name : "www.iostream.ir", platform : "www.fanoox.com") if _web_site_and_plat_form_one !== _web_site_and_plat_form_two { print("This is refrence!") }else { print("No,this is not refrence!") } مثال بالا هم که کاملا واضح است! اینکه اگر دو شئ با هم ارجاعی نداشتند، پس نتیجه طبق این آپِریِتر true است و در غیر اینصورت اگر ارجاع داشتند، نتجیه false و شرط آخر اجرا خواهد شد که No, this is not refrence است. آپِریِتر ترِنِری ( Ternary Conditonal Operators ) سه تا قسمت داره: مسئله/شرط مقدار اول مقدار دوم شرط در ابتدا قرار می‌گیره و سپس به دنبال آن آپِریِتر ? و بعد از آن جواب اول در صورت درست بودن شرط که برگشت داده می‌شود و علامت کالُن ( : ) در صورتی که نتیجه نادرست یا false باشد در شرط، مقدار بعد از کالُن/دونقطه ( : ) برگشت داده می‌شود. به مثال زیر دقت کنید: let _web_site_name_and_platform_name : String = 2 > 1? "www.iostream.ir" : "www.fanoox.com" print("Result =>", _web_site_name_and_platform_name) // Output the www.iostream.ir در مثال بالا همان‌طور که مشاهده می‌کنید، ابتدا شرط/مسئله قرار می‌گیرد و سپس مقدار بعد آپِریِتر ? و مقدار اول و دوم که با کالُن ( : ) از هم جدا می‌شوند. در این مثال نتیجه، www.iostream.ir است، چرا که ۲ بزرگتر از ۱ است و این از لحاض منقطی نتیجه‌ی true دارد. آپِریِتر دامِنه ( Range Operator ) این آپِریِتر در حلقه‌ها استفاده می‌شود و نحوه‌ی کار آن‌ را در جلسات قبل توضیح داده‌ام: for index_number_one in 0...20 { print(index_number_one, separator : " ",terminator : "") // Output the number 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 } for index_number_two in 0..<20 { print(index_number_two, separator : " ",terminator : "") // Output the number 0 1 2 3 4 5 6 7 8 9 10 11 12 13 15 16 17 18 19 } آپِریِتر‌های منطقی ( Logical Operators ) شامل آپِریِتر‌های !،&&،|| است. که بیشتر استفاده‌ی آن‌ها در حلقه‌هاست، اما شما می‌توانید در هر جایی از پروژه‌ی خودتان که نیاز به این آپِریِتر‌ها داشتید، به صورت مستقیم استفاده کنید. آپِریِتر ! ( نَقیض ) کار این آپِریِتر، برعکس کردن یا نَقیض کردن یک مقدار هستش که ممکنه true یا false باشه. به عنوان مثال اگه مقدار متغییری true بود و این آپِریِتر رو در ابتدای اون مقدار قرار دادید، خروجی برابر است با false. و همین مورد هم برعکس صدق می‌کند، یعنی اگر شما مقدار متغییری false داشتید، با گذاشتن این عبارت در ابتدای متغییر، نتیجه true می‌شود. این مثال زیر را ببینید: var is_name_web_site_iostream : Bool = false print(!is_name_web_site_iostream) // Output the bool true is_name_web_site_iostream = true print(!is_web_site_iostream) // Output the bool false آپِریِتر && اگر در شرط یا عبارتی بخواهیم دو شرط را بررسی کنیم که هر دو هم حتما باید درست ( true ) باشند از این آپِریِتر استفاده می‌کنیم. به مثال زیر دقت کنید: let web_site_name, platform_name : String web_site_name = "www.iostream.ir" platform_name = "www.fanoox.com" if web_site_name == "www.iostream.ir" && platform_name == "www.fanoox.com" { print(web_site_name, platform_name) } else { print("None") } اگر در مثال بالا، هر دو شرط مبنی بر اینکه دو مقدار متغییر تعریف شده باید برابر با مقدار تعیین شده در شرط باشند تا مقادیر چاپ شوند. در غیر اینصورت با خروجی None روبرور خواهیم شد. آپِریِتر || این یکی زیاد گیر نمیده که حالا حتما باید هر دو شرط با هم برقرار باشن! یکی از شرط‌ها در بین چندین شرط که برقرار باشه و به عبارتی نتیجه درست (‌ true ) برگردونه، وارد بدنه‌ی شرطه میشه و دستورات رو اجرا می‌کنه. اگه تمامی شرط‌ها نادرست (‌ false ) بودند، دستورات داخل بدنه اجرا نخواهند شد. به مثال زیر دقت کنید: var web_site_name, platform_name : String web_site_name = "www.iostream.ir" platform_name = "www.fanoox.com" if web_site_name == "www.iostream.ir" || platform_name == "www.fanoox.ir" { print(web_site_name, platform_name) } else { print("None") } در مثال بالا شرط دوم برقرار نیست! چرا که متغییر platform_name با مقدار سمت راست آن برابر نیست. اما از آنجایی که از آپرِیِتر ||‌ استفاده کردیم، پس شرط اول درست بوده و دستورات داخل بدنه‌ی شرط if اجرا می‌شوند. کلمات کلیدی continue,break,fallthrough کلمه‌ی کلید continue اگر بخواهیم در یک حلقه در یک جایی به بعد دستورات اجرا نشوند و مجددا حلقه اجرا شود، از این کلمه‌ی کلید کمک می‌گیرم. به مثال زیر دقت کنید: for index in 0...20 { if index == 5 { continue } print(index) } /* Output the number 0 1 2 3 4 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 */ همان‌طور که مثال بالا مشاهده‌ کردید، در حلقه‌ی for شرط داخل حلقه بر این مبنا بود که اگر متغییر index مساوی مقدار ۵ شد، ادامه‌ی دستورات را ادامه نده ( continue ) و مجدد حلقه را از سر بگیر. بخاطر همین است که عدد ۵ نمایش داده نشده. کلمه‌ی کلید break با این کلمه‌ی کلید به راحتی می‌توانید هر جایی از حلقه که دیگر نیازی نداشتید که حلقه ادامه ندهد، با این کلمه کلید حلقه را متوقف یا به اصطلاح بشکنید!. با این کار دیگر حلقه تکرار نشده و دستورات بعد از حلقه اجرا خواهند شد. به مثال زیر دقت کنید: for index in 0...5 { if index == 2 { break } print(index) } /* Output the number 0 1 */ کلمه‌ی کلید fallthrough شاید در جایی از دستوری شرطی switch لازم داشتید که caseهای بعدی هم اجرا شوند. با این کلمه‌ی کلیدی می‌توانید این کار را انجام دهید. به مثال زیر دقت کنید: let _number : Int8 = 1 switch _number { case 2: print(2) case 1: print(1) fallthrough case 3: print(3) fallthrough default: print("None") } بعد از هر caseی که می‌خواهید اجرا شود باید این کلمه‌ی کلید را قرار دهید. منتظر جلسه‌ی پنجم این دوره‌ی آموزشی باشید.
  17. 2 امتیاز
    درود بر شما؛ بله مشکل دارد، همانطوری که خودتان هم گفتید دارید آدرس یک متغیر محلی را از تابع بر می‌گردانید که این اخطار را هم از سمت کامپایلر هنگام کامپایل آن تابع دریافت می‌کنید ‌: $ warning: function returns address of local variable و برنامهٔ شما هم به احتمال زیاد Segmentation Falut داده و از بین می‌رود. برنامهٔ شما ممکن است که خروجی درستی نداشته باشد، چراکه دارید از یک حافظه‌ای داده را می‌خوانید که اصلاً وجود ندارد (حافظه آزاد شده است). در کامپایلر MSVC2017 و سیستم‌عامل Windows 7 64bit کد را برای شما کامپایل و بدون مشکل اجرا می‌کند امّا خروجی درستی ندارید. در کامپایلر MinGW و سیستم‌عامل Windows 7 64bit کد را کامپایل و اخطاری که در بالا اشاره کرده‌ام را داده و در هنگام اجرا برنامه با Segmentation Fault رو به رو می‌شود. کامپایلرهای GCC و‌ Clang در سیستم‌عامل ArchLinux اخطار بالا را داده و همانند MinGW عمل می‌کند. کامپایلر TCC در سیستم‌عامل ArchLinux نیز همانند MSVC2017 عمل می‌کند. بهتر است که تابع را به این شکل بازنویسی کنید : char* doXOR(char* cData1, char* cData2) { char* cData = malloc(256); assert(cData); for (int i = 0; i < 255; ++i) { cData[i] =cData1[i] ^ cData2[i]; } cData[255] = '\0'; return cData; } و همچنین موقع استفاده : int main (void) { /* ... */ char* tmp = doXOR(cData1, cData2); strcpy(cData3, tmp); printf("%s\n", cData3); free(tmp); tmp = NULL; return EXIT_SUCCESS; }
  18. 2 امتیاز
    سلام، افزونه‌ی Qt در Visual Studio تنها امکان توسعه‌ی برنامه‌های تحت کیوت رو در محیط ویژوال استودیو می‌دهد (بنابراین هماهنگی کامل با فناوری‌های اختصاصی کیوت را نخواهد داشت). در صورتی که شما کامپایلر و تنظیمات qmake یا cmake را برای پلتفرم‌های مورد نظر به درستی تنظیم کنید می‌تونید خروجی مناسب را تهیه کنید. دقت کنید که برای iOS و Linux شما باید روی پلتفرم‌های مک و لینوکس خروجی بگیرید. برای iOS و macOS بهترین روش همین هست که شما در پلتفرم مربوط به خودشون کامپایل کنید. حتی برای لینوکس هم بهتره از همین روش استفاده کنید (هرچند به کمک روش کراس کامپایل می‌تونید خروجی بگیرید). سعی کنید از استاندارد‌ها و رابط‌های خاص ویندوز استفاده نکنید، سعی کنید استاندارد‌های هر پلتفرم رو برای خودش اعمال و تحت چهارچوب و قوانین کیوت طراحی و توسعه انجام بدین. در این صورت بدون دردسر‌های متداول می‌تونید برنامه‌ی خودتون رو برای پلتفرم مقصد خروجی و اجرا کنید. این بخش هم مقالات و آموزش‌های مناسبی موجود هستند که پیشنهاد می‌کنم بررسی بفرمایید.
  19. 2 امتیاز
    @Alireza4 درود؛ اینکه چه کاری انجام بدهید و به کدام راه برید، تماماً بستگی به خودتان دارد. پیشنهاد می‌کنم که قشنگ درمورد کاری که می‌خواهید انجام بدید تحقیق کنید : - هدفتان از برنامه‌نویسی چیست ؟ - چقدر حوصلهٔ یادگیری مطالب را دارید ؟ - چقد دید سیستمی به برنامه‌نویسی دارید ؟ و ..، این مطلب را حتماً مطالعه کنید : کدام زبان برنامه‌نویسی را انتخاب کنم ؟ ، در این مقاله توضیحات لازم داده شده که بتونید خودتان تصمیم بر انتخاب زبان بگیرید. چرا که باید بدونید «درسته‌که زبان سی‌پلاس‌پلاس قدرت زیادی داره» امّا باید بدونید که هرچیز خوبی بالاخره هزینه‌ای هم داره و یادیگری کار کردن با این زبان به شش‌ماه تموم نمیشه، برای اینکه دراین‌باره هم بیشتر اطلاعات کسب کنید مقالهٔ چرا و چگونه باید ++C را یادبگیریم ؟ ، شاید اصلاً چیزی نبود که شما فکر می‌کردید.
  20. 2 امتیاز
    با توجه به پاسخی که در مورد دلیلش یافتید، در تکمیل روش جنابِ @فرهاد شیری روش‌های مختلفی برای تعاریف کلاس‌ها از نوع template در فایل cpp وجود دارد که قبل از C++17 روش زیر یکی از روش‌های رایج است که در آن شما کلاس را با انواع صریح تعریف می‌کنید. کد مربوط به فایل .h به صورت زیر خواهد بود: #ifndef ENTITY_H #define ENTITY_H #include <iostream> #include <string> template<class T> #define ENTITY_INT template class Entity<int>; #define ENTITY_STRING template class Entity<std::string>; #define ENTITY_BOOL template class Entity<bool>; #define ENTITY_DOUBLE template class Entity<double>; /*! * \brief The Entity class */ class Entity { public: Entity(); ~Entity(); /*! * \brief Function * \param t */ void Function(const T &t) const; }; #endif // ENTITY_H همچنین کد مربوط به فایل .cpp به صورت زیر پیاده سازی خواهد شد: #include "entity.h" template<class T> Entity<T>::Entity() { } template<class T> Entity<T>::~Entity() { } template<typename T> void Entity<T>::Function(const T &t) const { std::cout << "T = " << t << std::endl; } ENTITY_INT ENTITY_STRING ENTITY_BOOL ENTITY_DOUBLE روش استفاده: #include <iostream> #include "entity.h" int main(void) { Entity<int> *en = new Entity<int>; en->Function(10); delete en; Entity<double> *en2 = new Entity<double>; en2->Function(2000.23); delete en2; Entity<std::string> *en3 = new Entity<std::string>; en3->Function("Hello, World!"); delete en3; } نکته: در استاندارد ۱۷ استفاده از پارامتر‌های auto نیز برای بهینه نویسی بیشتر مفید هستند.
  21. 2 امتیاز
    سلام خب اول برات delete رو میگم که ساده تره . شما یه توی برنامه نویسی شئ گرا که کار با کلاس ها (class) پیش میاد. همانطوری که میدونید توابعی به صورت پیشفرض کامپایلر داخل کلاس تعریف میکنه. که یکی از این توابع اسمش سازنده (constructor) هست که وظیفه مقدار دهی کلاس را داره. این مثال رو در نظربگیرید : #include <iostream> class JustForTest{ private : int ClassVariable; public : JustForTest(const int& input) : ClassVariable(input){ } int ReturnClassVariable(){ return this->ClassVariable; } }; int main(){ JustForTest Object(10); std::cout << Object.ReturnClassVariable () << std::endl; return 0; } خب کاری که داخل این کلاس انجام شده اینکه یه کلاس به اسم JustForTeste تعریف شده که دارای یه متغیر و دو تابع عضو هست. که یکی از توابع ، تابع سازنده کلاس هست. این برنامه در اجرا هیچ مشکلی نداره و دقیقا همان چیزی که ما انتظار داریم را چاپ میکنه ، که مقدار ده هست. حالا مشکل وقتی پیش میاد که برنامه نویسی بیاد و از کلاس شما استفاده کنه و سهواً مقداری از نوع char به سازنده کلاس ارسال کنه : int main(){ JustForTest Object('3'); std::cout << Object.ReturnClassVariable () << std::endl; return 0; } بر خلاف انتظارمان این کد هم کامپایل میشه ، چرا که خود char نوعی از int هست. و مسلما خروجی نامناسبی هم داره. برای اینکه این مشکل رفع بشه اغلب برنامه نویس ها از این روش استفاده میکنن : class JustForTest{ private : int ClassVariable; JustForTest(const char& input){ } public : JustForTest(const int& input) : ClassVariable(input){ } int ReturnClassVariable(){ return this->ClassVariable; } }; به این صورت که سازنده ای با ورودی char را با نوع دسترسی private تعریف میکنن که باعث میشه برنامه نویس های دیگر نتوانن مقدار از نوع char به تابع شما ارسال کنن. اما ! واضحه که این روش زیاد جالب نیست و باعث ناخوانی و زشتی کد هم میشه. لذا بهتره که با استفاده از قابلیتی که سی پلاس پلاس فراهم کرده استفاده کنیم و این سازنده را از کلاس حذف کنیم. که این کار با استفاده از کلمه کلیدی delete صورت میگیره : class JustForTest{ private : int ClassVariable; public : JustForTest(const int& input) : ClassVariable(input){ } JustForTest(const char& input) = delete; int ReturnClassVariable(){ return this->ClassVariable; } }; به همین سادگی و بدون خون ریزی و اما کلمه کلیدی explicit : تقریبا ماننده همون مثالی هست که برای کلمه کلیدی delete در قسمت بالا زدم. همانطوری که دیدید با اینکه ما مقدار char به تابع سازنده کلاس ارسال کردیم بدون مشکل برنامه کامپایل و اجرا شد. اما اتفاقی که پشت صحنه افتاده این بوده که : کامپایلر خودش مقدار char را به int تبدیل (cast) کرده. برای اینکه از این تبدیل جلوگیری کنیم. از کلمه کلیدی explicit قبل از تابع سازنده استفاده میکنیم : #include <iostream> class JustForTest{ private : int ClassVariable; public : explicit JustForTest(const int& input) : ClassVariable(input){ } int ReturnClassVariable(){ return this->ClassVariable; } }; int main(){ JustForTest Object = '3'; std::cout << Object.ReturnClassVariable () << std::endl; return 0; } برنامه ی بالا کامپایل نخواهد شد. چرا که هیچ تابع سازنده ای با پارامتر char پیدا نشده ! اما ممکنه که سوال بپرسید : پس فرق explicit با delete در چی هست ؟ جواب سوال : اگه کمی به کد دقت کنید متوجه میشید که ما با استفاده از Copy initialization (یعنی استفاده از = برای مقدار دهی) سازنده کلاس را مقدار دادیم. ولی درصورتی که از direct یا uniform initialization استفاده کنیم کد ما همچنان کامپایل میشود.. مثال : int main(){ JustForTest Object('3'); std::cout << Object.ReturnClassVariable () << std::endl; return 0; } خب برای اینکه به طور کلی ما تابع سازنده ای با پارامتر char را محدود کنیم باید از کلمه کلیدی delete‌ استفاده کنیم. خلاصه : کلمه کلیدی explicit برای محدود کردن تبدیل نوع در پارامتر کلاس هست. اما این در زمانی اتفاق می افتد که ما از copy initialization برای مقداردهی سازنده کلاس استفاده کنیم. و در صورت استفاده از direct یا uniform initialization دیگه این عمل امکان پذیر نیست. و برای اینکه یک تابع را به کلی از کلاس محدود کنیم از کلمه کلیدی delete استفاده میکنیم که باعث میشود هیچ شئ توانایی فراخوانی آن تابع را نداشته باشد.
  22. 2 امتیاز
    «بخش دوم» در پست ۷ گام برای تبدیل شدن به یک طراح موفق UI/UX به طور خلاصه به اصول طراحی رابطه‌ی کاربری اشاره کردیم. در این بخش قصد داریم در مورد اصل تعادل (Balance) صحبت کنیم. هر طرح با یک صفحه‌ی خالی یا فضای خالی آغاز میشود. هنگامی که یک عنصر اضافه میکنیم، قرار دادن آن میتواند تعیین کند که طراحی تا چه حد موفق خواهد بود. طراحی موثر ارتباطات و علاقه‌ی بیننده را بنا میکند؛ خواه این طراحی به صورت چاپ شده و یا به صورت صفحات وب باشد. در یک طرح وب معمولی، طراح باید لوگو، متن، عکس یا تصویر را جایگزین کند. با یک تلاش آگاهانه برای ایجاد ارتباط بین این عناصر می‌توان یک طراحی چشم نواز به وجود آورد. تعادل در طراحی بسیار شبیه به تعادل در زندگی است. نمونه ای که اغلب از تعادل در دنیای واقعی یاد می‌شود، الاکنگ است. وقتی فقط یک نفر روی آن نشسته است، تجربه‌ی بسیار سرگرم کننده‌ای نیست. تعادل زمانی حاصل میشود که دو نفر از افراد با وزن مشابه در هر طرف قرار گیرند. نمونه‌ای دیگر از تعادل را هنگامی می‌توان به دست آورد که یکی از افراد سنگین در یک طرف و دو نفر سبکتر در طرف دیگر نشسته اند. اگر یک فرد سنگینتر به مرکز الاکلنگ نزدیکتر شود تعادل میتواند حاصل شود، در حالی که یک فرد سبکتر در انتهای طرف دیگر نشسته است. ما به عنوان انسان از لحاظ جسمی یک دست و پای در هر طرف ستون فقرات و سر داریم و قادر به ایستادن و حرکت با وجود اندازه و وزنهای مختلف هستیم. در طراحی سعی میکنیم به توازنی میان عناصر دست یابیم، زیرا در چشم نواز است. اما تعادل همیشه از طریق تقارن بدست نمیآید. نگاهی به جعبههای زیر کنید. خط سفید در جعبه‌ی 1 و 2 به طور متقارن متعادل است در حالی که در جعبه‌ی 3 و 4 خط به طور نامتقارن متعادل است. تقارن تعادلِ متقارن تعادلِ تصویر آینه است. اگر یک خط را از طریق مرکز صفحه بکشید، عناصر در یک طرف خط در سمت دیگر نیز به صورت آینه دیده میشوند. ما میتوانیم آن را با قرار دادن عناصر به طور نسبتا مساوی در طراحی دست یابیم. تقارن در طبیعت یک مثال معمول که در وب اتفاق میافتد، جایی است که بلوک های متن در سطر یا ستون به صورت آینه نگاشته می‌شوند. همچنین میتواند با استفاده از رنگ و تایپوگرافی به دست آید. برای مثال در وب سایت Mobile Web Book تصویر تلفن همراه این صفحه به دو بخش تقسیم می‌شود که با بلوکهای متن در هر طرف به صورت متعادل در مقابل هم قرار گرفته اند. در پوستر فیلم «The Day I Became A Woman»، بلوک متن سفید بزرگ در گوشه‌ی سمت راست بالا در گوشه‌ی پایین سمت چپ هم از لحاظ رنگ و هم از نظر شکل متقارن هستند. سایت پر طرفدار Florida Flourish تقریبا میتواند به نصف مرکز برسد که یک حس بسیار قوی از تعادلِ متقارن دارد. پوستر Havco که در زیر آورده شده است، قسمت چپ و راست پوستر با استفاده از اشکال مشابه و قطعات بدن متعادل است . متن قرمز در بالا و پایین عنوان در رنگ و اندازه‌ی متعادل قرار دارد. عدم تقارن طرح بندیهای متعادلِ نامتقارن دارای عناصری هستند که در یک خط مرکزی به صورت آینه‌ای جدا نمی‌شوند. این طرح بندی‌ها میتوانند طراحی را دشوارتر کنند، اما سبب جذابیت برای بیننده می‌شوند. میتوانیم با ایجاد چندین آیتم کوچک در یک طرف و یک آیتم بزرگ در طرف دیگر یک طرح متضاد نامتقارن ایجاد کنیم. اگر یک آیتم تیره در یک طرف دارید، میتوانید چندین آیتم روشن رنگی را در طرف دیگر قرار دهید. یک طراحی متعادلِ نامتقارن میتواند تنش ایجاد کند و بیننده را جذب کند. وب سایت MattWeb یک گرافیک بزرگ دارد که سمت چپ صفحه‌ی اصلی را پر کرده است. حس توازن نامتقارن در اینجا با استفاده از رنگهای تطبیق و یک فونت بدون سرصفحه مطابق با مارپیچ‌ها به دست می‌آید. سایت Dann Whitaker دارای چندین عنصر است که به صورت متقارن نیستند، اما از نظر رنگ، بافت و محتوا یکدیگر را متعادل میکنند. عدم تعادل (Off-Balance) خوب، پس اگر تمام کارهای طراحی شما در تعادل باشند، خسته کننده میشود. اگر قوانین را میدانید، میتوانید آن‌ها را قطع کنید، و عدم تعادل میتواند جنبش و حرکت را به بیننده منتقل کند. البته این مسئله میتواند بیننده را به کمی ناراحت و آشفته کند. طراحی غیر متعادل میتواند مخاطب را به فکر کردن وادار کند. فقط اجازه ندهید که به طور تصادفی این اتفاق بیفتد. در مقاله‌ی آینده نگاهی به اصل مجاورت (Proximity) میکنیم. در عین حال توصیه می‌شود که در وب سایت‌های مختلف تعادل قوی، تقارن و عدم تقارن را بررسی کنید.
  23. 2 امتیاز
    معمولاً در بازی سازی و یا تولید اپلیکیشن‌های کاربردی مدیریت رخداد‌ها بسیار مهم بوده و برای استفاده و نشان دادن واکنش بر یک عمل خاص نیازمند توابع و کلاس‌های پیچیده‌ای می‌باشد که در موتور‌های بازی سازی پیاده سازی می‌شوند. کلاس Event در قالب sf::Event تمامی اطلاعات رخداد از طرف سیستم را ارائه می‌دهد. برای مثال فشرده شدن کلیدی بر روی صفحه کلید و یا حرکات دسته‌ی بازی و مواردی از این قبیل را می‌توان توسط این ماژول مدیریت کرد. یک sf::Event به طور کلی نمونه ای از انواع رخداد‌ها را شامل می‌شود (حرکات ماوس، فشرده شدن کلید، بسته شدن پنجره و غیره...) همگی حزئیاتی از این رویداد می‌باشند. برای مثال وقتی شما یک رویداد فشرده شدن کلیدی را دریافت می‌کنید، بعد از آن باید عضوی از event.key و یا دیگر اعضای مرتبط با event.mouseMove و یا event.text را فراخوانی کنید. برای مثال: sf::Event event; while (window.pollEvent(event)) { // Request for closing the window if (event.type == sf::Event::Closed) window.close(); // The escape key was pressed if ((event.type == sf::Event::KeyPressed) && (event.key.code == sf::Keyboard::Escape)) window.close(); // The window was resized if (event.type == sf::Event::Resized) doSomethingWithTheNewSize(event.size.width, event.size.height); // etc ... } نوع شمارنده‌ی Closed زمانی مورد استفاده قرار می‌گیرد که پنجره درخواست بسته شدن را درخواست کرده باشد. شمارنده‌ی KeyPressed در هنگام درخواست کلیدی مورد استفاده قرار می‌گیرد. و یا Keyboard::Escape اشاره به فشرده شدن کلید Esc بر روی صفحه کلید را دارد. نوع Resized زمانی که پنجره درخواست تغییر اندازه را داشته باشد استفاده خواهد شد. نوع TextEntered زمانی که کاراکتری وارد شود عمل می‌کند. یا انواعی مانند JoystickConnected که در زمان اتصال دسته‌ی بازی عمل می‌کند. در ادامه لیستی از انواع شمارشی رویداد‌ها آورده شده است. فرض کنیم در محیط بازی یا برنامه‌ی خود قرار است مختصات x و y حرکت ماوس را دریافت نماییم. با استفاده از شرایط رخداد‌ها و به کارگیری کلاس sf::Mouse می‌توان این کار را به راحتی انجام داد. تابعی به صورت زیر خواهیم داشت: void MousePosition(){ sf::Mouse ms; std::cout << "Mouse Position X : " << ms.getPosition().x << "\t Y : " << ms.getPosition().y << std::endl; } کلاس Mouse با استفاده از توابع عضو با نام‌ getPosition().x و getPosition.y مختصات x و y حرکت ماوس در محیط پنجره ساخته شده را برمی‌گرداند. کُد تکمیلی: #include <SFML/Window.hpp> #include <iostream> void MousePosition(){ sf::Mouse ms; std::cout << "Mouse Position X : " << ms.getPosition().x << "\t Y : " << ms.getPosition().y << std::endl; } int main() { sf::Window window(sf::VideoMode(640, 480), "Hello world!"); while (window.isOpen()) { sf::Event event; while (window.pollEvent(event)) { if (event.type == sf::Event::KeyPressed) window.close(); } // check the type of the event... switch (event.type) { // window closed case sf::Event::Closed: window.close(); break; // key pressed case sf::Event::KeyPressed: std::cout << "Key pressed!" << std::endl; break; case sf::Event::MouseMoved: MousePosition(); break; // we don't process other types of events default: break; } } return 0; } کُد فوق را اجرا کرده و نتیحه آن با حرکت ماوس در داخل پنجره به صورت زیر خواهد بود: Mouse Position X : 746 Y : 567 Mouse Position X : 746 Y : 567 Mouse Position X : 746 Y : 567 Mouse Position X : 746 Y : 567 Mouse Position X : 729 Y : 567 Mouse Position X : 729 Y : 567 Mouse Position X : 729 Y : 567 Mouse Position X : 729 Y : 567 Mouse Position X : 729 Y : 567 Mouse Position X : 729 Y : 567 Mouse Position X : 729 Y : 567 Mouse Position X : 729 Y : 567 Mouse Position X : 729 Y : 567 Mouse Position X : 707 Y : 561 Mouse Position X : 707 Y : 561 Mouse Position X : 707 Y : 561 Mouse Position X : 707 Y : 561 Mouse Position X : 707 Y : 561 Mouse Position X : 707 Y : 561 Mouse Position X : 707 Y : 561 Mouse Position X : 679 Y : 551 Mouse Position X : 679 Y : 551 Mouse Position X : 679 Y : 551 Mouse Position X : 679 Y : 551
  24. 1 امتیاز
    آموزش زبان برنامه‌نویسی سوئیفت - جلسه اول مواردی که در این جلسه یاد خواهید گرفت: مقدمه زبان برنامه‌نویسی سوئیفت ، نوشتن اولین دستور و معرفی متغییر‌ها با سلام و عرض ادب خدمت شما دوستان عزیز و همراهان خوب همیشگی وب‌سایت آیوٌ اِسترِیم. در خدمت شما هستم با یک دوره‌ی جذاب برنامه‌نویسی به زبان سوئیفت! می‌دونم که علاقه‌مندان زیادی به توسعه‌ی محصولات برنامه برای محصولات شرکت اَپل دارن و از این رو با مشکلاتی هم روبرو هستند که یکیش همین نبود آموزش کامل و به بیانی ساده هستش! مورد دومی هم که خیلی تابلو هستش، همین تَحریمه که به جز اینکه دست قِشر کم‌درآمد جامعه رو بسته شامل تحریم‌های دیگه هم هست. و حالا خیلی‌ها بخاطر علاقه به یادگیری نمی‌تونن محصولات اَپل رو خریداری کنن و شروع کنن به توسعه‌ی محصولات نرم‌افزاری شگفت‌اَنگیز! از این رو تنها یک راه وجود داره اونم استفاده از نسخه‌ی هک‌شده‌ی سیستم عامل مَک هستش که به شما امکان استفاده از امکانات یک سیستم‌عامل مَک واقعی رو میده! البته هدف بنده این نیست که بگم استفاده از این روش خوبه! بلکه برای آن دسته از عزیزانی که توانایی خریدن مَک‌بوک و یا سایر محصولات اَپل رو ندارن گفتم وگرنه اگه توانایی خرید دارید که بهترین راهش همینه که بخرید و لذت یک سیستم‌عامل متفاوت و جدید رو داشته باشید :)). خُب،‌ بریم سر اصل مطلب! اینکه در این دوره‌ی آموزش چه چیز‌هایی رو یاد می‌گیرد،‌ فقط و فقط سه چیز هست؛ مقدمات یادگیری Syntax ( سِینتَکس) زبان و کدنویسی مقدماتی یادگیری ‌رابط‌کاربری ( User Interface ) ایجاد یک پروژه‌ی ساده ماشین‌حساب و بعد از این مباحث هم کُلیت کار دستتون میاد و به راحتی ‌می‌تونید از منابعی مُعتبر،‌ دانش و مهارت خودتون رو بالا ببرید. زبان برنامه‌نویسی سوئیفت ( Swift ) چیست ؟ سوئیفت یک زبان‌ برنامه‌نویسی از نوع کامپایلری برای توسعه محصولات نرم‌افزاری macOS , iOS , watchOS و tvOS که توسط شرکت اَپل ساخته شده است. قبل از این از زبان برنامه‌نویسی Objective-C برای توسعه محصولات برای موارد ذِکر شده استفاده می‌شد که بعد از آن این زبان جایش را به سوئیفت داد اما همچنان از Objective-C هم استفاده می‌شود. هدف در اینجا آموزش زبان است و شما می‌توانید برای توضیحات بیشتر به این اینجا مراجعه کنید. شروع کد‌نویسی برای شروع کدنویسی به زبان‌برنامه‌نویسی سوئیفت می‌تونید از یک برنامه‌ موبایل هم حتی استفاده کنید! البته تنها در بخش مقدماتی. نام این نرم‌افزار موبایلی Sedona Swift Compiler است که می‌تونید از فروشگاه Play دانلود و نصب کنید. یا از نرم‌افزار‌ی ساده بر روی ویندوز خود کُد‌نویسی را شروع کنید که هم‌ این‌کار هم برای لینوکس صِدق می‌کند که با نصب یک بسته می‌توانید در لینوکس هم کُد‌نویسی با این زبان شیرین را شروع کنید. یا در نهایت اگر مَک‌بوک دارید که چه بهتر و اگر ندارید از نسخه‌ی هک‌شده‌ی آن استفاده کنید که عرض کردم در موقعی که واقعا چاره‌ای ندارید!. اولین چیز در هر زبان برنامه‌نویسی که آموزش داده می‌شود؛ هِلوُ وُرلد (‌ !Hello World ) خودمونه!. پس این کُد ساده رو ببیند که چقدر قَشنگ این پیغام رو در کُنسول چاپ می‌کنه:‌ print("Hello World!") به همین سادگی که مشاهده کردید، با استفاده از متد print پیغام Hello World رو چاپ کردیم. اگر این دستور رو اجرا کنید، با همین پیغام در کُنسول مواجه میشید. پس این تابع برای چاپ مقادیر در سوئیفت هستش. شاید دقت کرده باشید که سِمی‌کالُن نذاشتم! سوئیفت این اجازه‌ رو میده که بدون گذاشتن سِمی‌کالُن برناممون رو اجرا کنیم که البته بذارید هم مشکلی پیش نمی‌آید، مگر در موقعی که چندین دستور در یک خط داشته باشید که اون موقع واجب میشه! تابع print یک تابع سراسری در سوئیفت هستش که چندین آرگومانت دریافت می‌کنه و اساس کارش، چاپ اطلاعاتی هستش که بهش میدیم. و حالا یه سری آرگومانت‌های پیش‌فرض‌ هم میگیره که می‌تونیم بسته به نیاز اون‌ها را تغییر بدیم. در اولین آرگومانت، ما تا هر چند‌تا مقادیر یا همان آیتم‌ها که بخواهیم می‌توانیم وارد کنیم و در خروجی نمایش دهیم. به این شکل که می‌بینید: print("www.iostream.ir", "www.fanoox.com", item3, item4, ...) در آرگومانت دوم، که separator هستش، می‌توانیم مشخص کنیم که اگر دارای چندین آیتم اطلاعات بودیم، در بین هر کدام از این اطلاعات، چه نمادی/علامتی قرار گیرد؟. که ما می‌تونیم اون نماد/علامت رو در بین دو "" مشخص کنیم. به عنوان مثال:‌ print("www.iostream.ir", "www.fanoox.com", separator : " :)) ") // Output the ==> www.iostream.ir :)) www.fanoox.com هما‌نطور که مشاهده کردید، می‌توانیم از هر نماد/علامتی که دوست داشتیم در بین انبوهی از داده‌ها استفاده کنیم. در آرگومانت سوم، می‌توانیم مشخص کنیم که اطلاعات به خط بعدی ( new line ) یا در همان خط فعلی چاپ شوند یا خیر که به صورت زیر است: print("www.iostream.ir", terminator : "") print("www.fanoox.com") /*Output the => www.iostream.ir www.fanoox.com that not of include is new line */ در حالت پیش‌فرض تابع به صورت "terminator : "\n هستش که به معنی در " در پایان چاپ اطلاعات، اطلاعات دیگر را که بعد از این مقادیر چاپ‌شده می‌آیند را چاپ کن ". برای تعریف متغییر در سوئیفت به دو صورت می‌تونید عمل کنید: تعریف متغییر بدون تعیین نوع تعریف متغییر با تعیین نوع همچنین ما دو نوع متغییر داریم: متغییری که مقدارش می‌تونه در ادامه‌ی برنامه تغییر کنه متغییر که مقدارش فقط در هنگام تعریف مشخص و قابل تغییر در سراسر برنامه نیست ( ثابت‌ها ) برای تعریف متغییر بدون نوع به این صورت عمل می‌کنیم:‌ var website_name = "www.iostream.ir" print(website_name) // or print("The website name is \(website_name)") همانطور که مشاهده می‌کنید تعریف یک متغییر که همواره مقدارش تغییر کنید با کلمه‌ی کلید var تعریف می‌شود و بعد از آن نام و مقدار آن می‌آید. در این حالت کامپایلر به صورت ضمنی خودش از رو مقدار، نوع متغییر را متوجه می‌شود و اگر شما این دستور را بنویسید: var website_name = "www.iostream.ir" print(type(of : website_name )) // or print("Type is => \(type(of : website_name )") // Result => String به شما مقدار String یا همان رشته‌ای را می‌دهد. اما در حالت تعیین نوع برای متغییر به این صورت است: var website_name : String = "www.iostream.ir" print(webiste_name) // or print("The website_name is \(website_name)") که شما صراحتا نوع را مشخص کردید و کامپایلر در اینجا و در ادامه‌ی برنامه‌ی مقدار غیری از String را قبول نمی‌کند. همان‌طور که مشاهده می‌‌کنید،‌ برای تعریف نوع برای متغییر باید از دو کالُن ( : )‌ استفاده کنیم و سپس حرف اول نوع متغییر را بزرگ بنویسم ( البته در این زبان ) مانند؛‌ Int و سپس با گذاشتن علامت انتساب ( = ) مقدار مورد نظر خود را به آن بدهیم. نکته‌ای دیگری که وجود دارد در چاپ کردن مقادیر است که شما می‌توانید با دو روش فوق که ذکر شده‌ است، مقادیر را چاپ کنید که یکی بصورت آیتم به آیتم یعنی print( item1, item2, item3 , ...) و تا هر چند آیتم را که حاوی مقادیر هستند به خروجی بفرستید و نمایش دهید و اما در حالت دوم باید Syntax متفاوتی استفاده کنید و آن هم ادغام رشته با مقادیر متغییر‌هاست! که مثالش را بالا برای شما عزیزان زده‌ام و مقادیر متغییر‌ها باید بین دو پرانتز و قبل پرانتز از یک بک‌اسلش رو به عقب استفاده کنید!‌. و اما بریم سراغ ثابت‌‌ها! از اسم این متغییر‌ها پیداست که یک بار تعریف می‌شوند و مقدار ثابتی دریافت می‌کنند و در ادامه‌ی برنامه و یا رَوند پُروژه هیج تغییری نمی‌کنند و در طول برنامه یا پروژه مقادیرشون ثابته! بیاید با یک مثال شروع کنیم؛ ثابت‌ها در سوئیفت با کلمه‌ی کلید let تعریف می‌شوند و همانند متغییر‌ها شامل نام و نوع هم می‌شوند: let _website_name = "www.iostream.ir" _webiste_name = "iostream.ir" // Error , beacuse it's a constant print(_website_name) // or print("The website name => \(_website_name)") هما‌نطور که می‌بینید، تغییر دادن مقادیر ثابت‌ها باعث بروز خطا میشه و اجازه‌ی چنین کاری به شما داده نمیشه. و برای تعریف با تعیین نوع هم به این شکل عمل می‌کنیم: let _number : Int = 20 _number = 40 // Error , beacuse it's a constant print(_number) // or print("The number => \(_number)") تا همین‌جا کفایت می‌کنه و تا شما یک قهوه نوش‌جان کنید و این کد‌ها رو آزمایش کنید و همچنین از صِحَت نتیجه اطمینان حاصل کنید، منم یه کوچولو استراحت می‌کنم و جلسه‌ی دوم رو که شامل نحوه‌ی نام‌گذاری صحیح برای متغییر‌ها و ثابت‌ها و نام‌گذاری‌های مجاز و همچنین یکم بحث رو می‌کشونیم سمت دستورات شرطی و حلقه‌ها. امیدوارم این جلسه از دوره‌ی آموزش زبان برنامه‌نویسی سوئیفت مورد رضایت شما عزیزان واقع شده باشه. هر سوال و ابهامی داشتید، می‌تونید کامنت کنید تا براتون توضیح بدم. در پناه حق، هر کجای کشور عزیزمون ایران هستید، شاد، سرافراز و موفقیت روز‌افزون داشته باشید.
  25. 1 امتیاز
  26. 1 امتیاز
    منظور محدود کردن نیست، در واقع شما عمل تأیید یا امضاء برای یک شیء را جهت استفاده در یک کتابخانه یا برنامه‌ی دیگر اعمال می‌کنید. این واژه‌ی کلیدی به شما اجازه می‌دهد اطلاعات کلاس ذخیره شده را در مراحل کامپایل به لینکر مشخص کنید، با توجه به مثال مربوطه استفاده از این دستور اساساً با توجه به صِفتِ dllexport به لینکر می‌گوید که شیء مورد نظر برای استفاده صادر می‌شود که توسط سایر کتابخانه‌ها (DLL) یا برنامه‌ها قابل استفاده خواهد بود. در این حالت تعاریف مربوط به اعلان‌های موجود باید در برنامه‌ی مشابه پیاده سازی شود، در غیر اینصورت خطای لینکر ساطع می‌شود. زمانی از این روش استفاده می‌شود که بخواهید DLL ای را ساخته و دیگر موارد را به آن لینک (پیوند) کنید. در صورتی که صفت dllimport مشخص شود عمل عکسِ آن صورت می‌گیرد؛ پیاده‌سازی‌های (implementation) مربوطه به آن برای استفاده در برنامه‌ی شما وارد می‌شوند. استفاده از dllimport نیز یک گزینه‌ی اختیاری است. در صورت استفاده از این کلیدواژه، کامپایلر کد کارآمد‌تری را تولید می‌کند. با این حال بهتر است شما برای دسترسی به نماد‌ها و اشیاء داده‌های عمومی از آن استفاده کنید. به مثال‌های زیر توجه کنید: __declspec(dllexport) void messageBox (std::string_view msg); __declspec(dllimport) void messageBox (std::string_view msg); معمولاً پیشنهاد می‌شود برای داشتن یک API خوب به صورت زیر پیاده سازی شود. #ifdef LIBRARY_API #define LIBRARY_API __declspec(dllexport) #else #define LIBRARY_API __declspec(dllimport) #endif نمونه مثال از فایل سرآیند: #ifndef MESSAGELIB_HPP #define MESSAGELIB_HPP #pragma once #include <iostream> #include <string_view> #include <string> #ifdef LIBRARY_API #define LIBRARY_API __declspec(dllexport) #else #define LIBRARY_API __declspec(dllimport) #endif class LIBRARY_API MessageBox { public: MessageBox(); ~MessageBox(); std::string_view message(std::string_view msg); }; #endif // MESSAGELIB_HPP تعاریف مربوطه: #include "messagelib.hpp" MessageBox::MessageBox() { } MessageBox::~MessageBox() { } std::string_view MessageBox::message(std::string_view msg) { return msg; } استفاده از کتابخانه در برنامه‌ها یا کتابخانه‌های دیگر: #include <iostream> #include "messagelib.hpp" using namespace std; int main() { MessageBox msg; cout << msg.message("Hello Boy!") << endl; std::cin.get(); return 0; }
  27. 1 امتیاز
    درود، واژه‌ی کلیدی__decspec با صفتِ dllexport به شما اجازه می‌دهد تا توابع و متغیر‌ها را برای اعلان در مرحله‌ی ورود و خروج کنترل کنید. زمانی که بخواهید داده‌ها، توابع، کلاس‌ها و یا توابع عضو کلاس را از طرف DLL مورد استفاده قرار دهید از این کلمه‌ی کلیدی استفاده می‌شود.
  28. 1 امتیاز
    فعلاً به بلوغ نرسیده، باید در سمت مرورگر‌ها و سرور بهینه‌سازی بشه.
  29. 1 امتیاز
    باسلام تقریبا مدت زیادی است که درگیر Socket programming و Thread هستم. و هر چند وقت یکبار سوالاتی را در مورد چگونگی استفاده از آنها در فروم های مختلف مطرح و تا حدودی پاسخ خود را یافته ام. با توجه به اینکه گذشت زمان سوالات و شرایط تغییر کرده و لذا خواستم سوالاتی از این دست را یکبار دیگر از ابتدا مطرح و جواب کاملی برای هر کدام داشته باشم. اگر چه امکان دارد جواب هرکدام از این سوالات بطور پراکنده و جداگانه در سایتهای مختلف موجود باشد. علاوه بر آن کمتر جایی یدم که این دو موضوع را با هم بررسی کرده باشند یا فقط به مبحث Thread پرداخته اند ویا فقط درباره Socket توضیح داده اند آن هم فقط در مورد بعضی از توابع مرتبط. حتما دوستان با ترتیب اجرای دستورات و نحوه ایجاد و مفهوم سوکتهای سمت سرور و کلاینت آشنا هستند و هفت نحوه استفاده از آنها نیست بلکه هدف این است اگر من بخواهم از این توابع در Thread های مختلف استفاده کنم آیا امکان پذیر است. آیا پاسخ های داده شده برای محیط Windows, Linux, ... هردو درست است علاوه بر کتابخانه های همراه کمپایلرها کتابخانه های دیگری نظیر Boost - Poco - ACE - Qt - ... هم کتابخانه ای برای کار با شبکه دارند وضعیت توابع مشابه در آنها چگونه است. سوالات رو از سمت سرور شروع میکنم. فرض لازم است سمت سرور 3 سوکت سروری داشته باشیم. SOCKET AcceptSock1; SOCKET AcceptSock2; SOCKET AcceptSock3; حال باید دستورات زیر اجرا شود int InitFunction(SOCKET &AcceptSock) { SOCKET AcceptSock = socket(AF_INET, SOCK_STREAM, IPPROTO_IP); if (AcceptSock == INVALID_SOCKET) { printf("Function socket failed with error : %u\n", WSAGetLastError()); return 0; } int iTimeout = 500; BOOL option = TRUE; int iResult = setsockopt(AcceptSock, SOL_SOCKET, SO_RCVTIMEO, (char *)&iTimeout, sizeof(iTimeout)); if (iResult == SOCKET_ERROR) { printf("Function setsockopt failed with error: %u\n", WSAGetLastError()); return 0; } SOCKADDR_IN client_sin; SOCKADDR_IN local_sin; int iAddrSize = sizeof(client_sin); int iPort = 12345; // select the local interface, and bind to it local_sin.sin_addr.s_addr = htonl(INADDR_ANY); local_sin.sin_family = AF_INET; local_sin.sin_port = htons(iPort); if (bind(AcceptSock, (struct sockaddr *)&local_sin, sizeof(local_sin)) == SOCKET_ERROR) { printf("Function bind failed with error: %u\n", WSAGetLastError()); return 0; } if (listen(AcceptSock, 32) == SOCKET_ERROR) { printf("Function listen failed with error: %u\n", WSAGetLastError()); return 0; } } فانکشن بالا باید برای AcceptSock1 و AcceptSock2 و AcceptSock3 اجرا شود. با اجرای سه کد زیر. InitFunction(AcceptSock1); InitFunction(AcceptSock2); InitFunction(AcceptSock3); سوال آیا من میتوانم هرکدام از سه دستور بالا را در Thread های مختلف اجرا کنم ویا باید حتما در Thread main اجرا شود. برای پذیرش کلاینتهایی که درخواست اتصال دارند از تابع زیر استفاده میکنیم int ProcessFunction(SOCKET &AcceptSock) { fd_set fd; timeval tv; SOCKADDR_IN client_sin; int iAddrSize = sizeof(client_sin); while (true) { tv.tv_sec = 3; tv.tv_usec = 0; FD_ZERO(&fd); FD_SET(AcceptSock, &fd); int iResult = select(0, &fd, NULL, NULL, &tv); if (iResult == SOCKET_ERROR) { printf("Function select failed with error: %u\n", WSAGetLastError()); closesocket(AcceptSock); return 0; } if (iResult == 0) { continue; } SOCKET soc = accept(AcceptSock, (struct sockaddr *) &client_sin, &iAddrSize); if (soc == INVALID_SOCKET) { printf("Function accept failed with error: %u\n", WSAGetLastError()); continue; } //... Do } } که باید برای AcceptSock1 و AcceptSock2 و AcceptSock3 اجرا شود. با اجرای سه کد زیر. ProcessFunction(AcceptSock1); ProcessFunction(AcceptSock2); ProcessFunction(AcceptSock3); سوال سوال آیا من میتوانم هرکدام از سه دستور بالا را در Thread های مختلف اجرا کنم کلا لازم نیست هیچ گونه عملیات لاکی صورت گیرد. آیا من میتواند از AcceptSock1 در دو Thread مختلف استفاده کنم (پاسخ خیر است فقط میخواستم جواب در اینجا باشد). اگر استفاده کنم چه اتفاقی پیش میآید. فانکشن ()WSAGetLastError قطعا Threadsafe نیست ولی آیا توابع دیگری که کد خطا را بروز میکنند Threadsafe نیستند. برای خواندن و نوشتن اطلاعات از Socket پذیرش شده از توابع زیر استفاده می کنیم int SendMessageFunction (SOCKET sock, const char * msg, int len) { fd_set fd; timeval tv; tv.tv_sec = 0; tv.tv_usec = 0; FD_ZERO(&fd); FD_SET(sock, &fd); int iResult = select(0, NULL, &fd, NULL, &tv); if (iResult == SOCKET_ERROR) { printf("Function SendMessage failed with error: %u\n", WSAGetLastError()); return -1; } int rc = send(sock, (char *)msg, len, 0); if (rc != len) { return -1; } return 1; } int ReceiveMessageFunction(SOCKET sock, char * msg, int &len) { fd_set fd; timeval tv; tv.tv_sec = 0; tv.tv_usec = 0; FD_ZERO(&fd); FD_SET(sock, &fd); int i = select(0, &fd, NULL, NULL, &tv); if (i == SOCKET_ERROR) { printf("Function ReceiveMessage failed with error: %u\n", WSAGetLastError()); return -1; } else if (i == 0) { // no data on socket return 0; } // //... //len = ... //... int rc = recv(sock, (char *)msg, len, 0); // ... return 1; } آیا من میتوانم تاوابع بالا را در Threadهای مختلف استفاده کنم و یا خیر. آیا امکان خواندن از یک سوکت توسط دو Thread مختلف امکان پذیر است. نوشتن چطور آیا میتوان یک Thread از Socket بخواند و Thread دیگری در آن بنویسد. با توجه به این موضوع که در یکی از صفحات اینترنتی بیان شده بود که Boost برخلاف کتابخانه های دیگر Threadsafe است (اگرچه امروز هرچه گشتم دوباره آنرا پیدا نکردم) پاسخ با توجه به پلتفورم )Windows, Linux, ..) و کتابخانه مورد استفاده (پیشفرض کمپایلر - ACE - Poco - Boost Qt - ...) در نظر گرفته شود. متشکرم
  30. 1 امتیاز
    با سلام تابع خاصی برای این کار ندیدم ولی تیکه کد زیر کار میکنه براتون نوشتمش : QByteArray data = {"test values are good"}; qDebug() << data; int bufSize = 3; int dataSize = data.size(); dataSize = dataSize % bufSize ? dataSize / 3 + 1 : dataSize / 3; for (int i = 0; i < dataSize; ++i) { qDebug() << data.mid(i * bufSize, bufSize); }
  31. 1 امتیاز
    این تکنیک پایین هم میشه استفاده کرد QTimer::singleShot(1000, [this]() { //code 1 } ); QTimer::singleShot(2000, [this]() { //code2 } );
  32. 1 امتیاز
    فعال‌سازی مشاوره‌های ویژه در صفحه‌ی اینستاگرام و تلگرام.
  33. 1 امتیاز
    نام فایل‌های تست واحد فایل‌های تست را هم‌نام با کامپوننتی که تست می‌کند نام‌گذاری کنید. فایل‌های تست را با پسوند .spec. نام‌گذاری کنید. چرا؟ راهی ثابت را برای شناسایی تست‌ها فراهم می‌کند. چرا؟ الگوی هم‌گام با karma یا راه‌انداز‌های تست دیگر فراهم می‌کند. نام فایل‌های تست end to end فایل‌های تست e2e را به دنبال نام امکاناتی که تست می‌کند، با پسوند .e2e-spec. نام‌گذاری کنید. چرا؟ راهی ثابت برای شناسایی سریع فایل‌های تست e2e فراهم می‌کند. چرا؟ الگویی را مطابق راه‌اندازهای تست و سیستم‌های build خودکار فراهم می‌کند. نام ماژول (Module) های انگولار به نام نشانه‌ی ماژول پسوند Module را متصل کنید. به نام فایل پسوند .module.ts را بدهید. ماژول‌ها را با توجه به نام امکاناتی که ارائه می‌دهد و پوشه‌ای که در آن قرار دارد نام‌گذاری کنید. چرا؟ راهی ثابت را برای شناسایی و اشاره به ماژول‌ها فراهم می‌کند. چرا؟ نامگذاری شتری بزرگ ( Upper camel case) برای شناسایی اشیائی که می‌توانند توسط سازنده (constructor) نمونه سازی شوند عمومی است. ‌‌چرا؟‌ به راحتی ماژول را به عنوان ریشه‌ی امکاناتی که به همان شکل نامگذاری شده‌اند مشخص می‌کند. ماژول‌های روتینگ (routing) را با پسوند RoutingModule نام‌گذاری کنید. نام یک فایل حاوی ماژول روتینگ را با routing.module.ts تمام کنید. چرا؟ یک ماژول روتینگ (RoutingModule) ماژولی است که به صورت اختصاصی برای تنظیم کردن روتر (router) انگولار استفاده‌ می‌شود. یک قرارداد ثابت برای نام‌گذاری کلاس‌ها و نام فایل‌ها باعث می‌شوند این ماژول‌ها به راحتی پیدا شده و شناسایی شوند.
  34. 1 امتیاز
    بسیار ممنون از پاسخ مفید و ععال شما !
  35. 1 امتیاز
    اول باید کتابخانه بیلد بشه، با دستورات زیر: tar xzf libarchive-xxx.tar.gz cd libarchive-xxx ./configure make make check make install برای افزودن این کتابخانه به Qt از بخش Add Library پروژه اقدام کن، خروجیش چیزی شبیه به این باید بشه من آخرین نسخه رو امتحان کردم: LIBS += -L$$PWD/../../../Downloads/libarchive-3.3.3/build/ -larchive INCLUDEPATH += $$PWD/../../../Downloads/libarchive-3.3.3 DEPENDPATH += $$PWD/../../../Downloads/libarchive-3.3.3 PRE_TARGETDEPS += $$PWD/../../../Downloads/libarchive-3.3.3/build/libarchive.a در نهایت یک کد آزمایشی به صورت زیر برای زیپ کردن در قلب .tar امتحان کردم که مشکلی نداره و کار می‌کنه. فرصت زیادی ندارم تا همه مواردش رو چک کنم، شما طبق مستندات و مثال‌های پیش‌فرض امتحان و به کیوت اضافش بکنید. #include <QCoreApplication> #include <QString> #include <QByteArray> #include <QFileInfo> #include <QDebug> #include <QDirIterator> #include <libarchive/archive.h> #include <libarchive/archive_entry.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> int main() { QString directory = "iostreamir"; struct archive *a; struct archive_entry *entry; struct stat st; char buff[8192]; size_t bytes_read; int fd; QByteArray outArray = directory.toLocal8Bit() + ".tar"; char *outDirectory = outArray.data(); qDebug() << outDirectory; QByteArray inputArray = directory.toLocal8Bit(); char *inputDirectory = inputArray.data(); qDebug() << inputDirectory; QFileInfo inputInfo; inputInfo.setFile(directory); // the name of the directory QByteArray pathArray = inputInfo.fileName().toLocal8Bit(); char *pathDirectory = pathArray.data(); qDebug() << pathDirectory; a = archive_write_new(); archive_write_add_filter_gzip(a); archive_write_set_format_pax_restricted(a); archive_write_open_filename(a, outDirectory); QDirIterator it(directory, QDirIterator::Subdirectories); while (it.hasNext()) { entry = archive_entry_new(); stat(inputDirectory, &st); archive_entry_set_pathname(entry, it.next().toLocal8Bit().constData()); archive_entry_set_filetype(entry, AE_IFDIR); archive_entry_copy_stat(entry, &st); archive_write_header(a, entry); fd = open(inputDirectory, O_RDONLY); bytes_read = read(fd, buff, sizeof(buff)); while (bytes_read > 0) { archive_write_data(a, buff, bytes_read); bytes_read = read(fd, buff, sizeof(buff)); } close(fd); archive_entry_free(entry); archive_write_finish_entry(a); archive_write_close(a); archive_write_free(a); } return 0; }
  36. 1 امتیاز
    سلام چندتا سوال داشتم در زمینه اتصال دیتابیس به پروژه کوئیک من تا اینجا متوجه شدم که برای اتصال دیتابیس ابتدا باید یک فایل مختص درج و حذف و ویرایش و اتصال دیتابیس درست کرد. و یک فایل برای مدل. اولی که مشکلی ندارم و کاملا واضحه میمونه دومی: QML - Lesson 016. SQLite database and the working with it in QML Qt ۱ - رول Role چه استفاده ای داره؟ نام هایی که انتخاب می‌کنه چه کاربردی داره؟ ۲ - هش برای چی هست؟ ۳ - تابعی که خروجی QVariant هست برای چی تعریف شده؟ ۴ - ایا به ازای تمامی جداولی که در دیتابیس است باید رول تعریف شه؟ ۵ - زمانیکه چندین جدول داریم چجوری این فایل مدل باید تعریف بشه. ممنون میشم برام ساده توضیح بدید.
  37. 1 امتیاز
    با سلام تا جایی که به خاطر میارم مایکروسافت هم دیتابیسش رو کراس کرد. ولی در کل انتخاب خوبی نیست MS SQL Server ولی برای انتخاب دیتابیس کلا توی هر پروژه ای باشه باید اول بررسی کنید و میزان نیاز واقعیتون رو مشخص کنید. مثلا اگر بخواید روی موبایل هم خروجی بدید و تمایلی به تغییر دیتابیس نداشته باشید و فقط یک یا چند کاربر به صورت متوالی از نرم افزار استفاده کنند اس کیو لایت میتونه انتخاب خوبی باشه چون روی موبایل هم جواب میده و برای کار تک کاربره (کاری که هم زمانی نداشته باشه توش ) خوبه ولی ممکنه بخواید فقط روی دسکتاپ خروجی بدید و نیاز باشه که حالت سرور داشت هباشه نرم افزار و از چندین کلاینت بهش دسترسی باشه اونطوری بهتره از Mysql یا mariadb یا ... استفاده کنید. در جایی ممکنه بحث سنگین تر باشه که لازم بشه از NOSQL استفاده کنید که البته با توجه به عنوان پروژه شما بعید میدونم لازم بشه. موفق باشید.
  38. 1 امتیاز
    سلام و خسته نباشید. جناب اسدزاده باتشکر از رسیدگی شما. این مشکل در پست ارسال شده حل شد. اما همچنان در بخش ویرایشگر که میخواهیم کد را ارسال کنیم هیچ شماره خطی نمایش داده نمیشود.
  39. 1 امتیاز
    با سلام ! دو قطعه برنامه ی زیر یکی آرایه ۱۰۰۰۰۰ تایی در حافظه Stack ساخته و به صورت Random مقداردهی شده و Sort میشود. کد اول به زبان ++C و با استاندارد 11 نوشته شده است که حدود ۷ دقیقه و کد دوم به زبان C نوشته شده است که حدود 1 دقیقه زمان میبرد ! CPU : Intel i7 M 620 (4) @ 2.667GHz آیا راهی برای بهینه سازی سرعت اجرای برنامه ی نوشته شده به زبان ++C هست ؟ کد نوشته به زبان ++C : #include <iostream> #include <functional> #include <utility> #include <array> #include <random> #include <chrono> const unsigned int MAX_LENGTH = 100000; bool Compare(unsigned int FirstVariable,unsigned int SecondVariable){ if(FirstVariable < SecondVariable) return true; return false; } void SortArray(std::array<unsigned int,MAX_LENGTH> &MyArray,std::function<bool(unsigned int,unsigned int)> function){ for(unsigned int index=0;index < MAX_LENGTH;++index) for(unsigned int AnotherIndex=0;AnotherIndex<MAX_LENGTH;++AnotherIndex) if(function(MyArray[index],MyArray[AnotherIndex])) std::swap(MyArray[index],MyArray[AnotherIndex]); } void PrintArrayElements(const std::array<unsigned int,MAX_LENGTH> &MyArray){ for(const auto &item : MyArray) std::cout << item << std::endl; } void RandomizeArray(std::array<unsigned int,MAX_LENGTH> &MyArray){ std::mt19937_64 Random(static_cast<int>(std::chrono::high_resolution_clock::now().time_since_epoch().count())); std::uniform_int_distribution<> RandomGenerator(0,1000); for(unsigned int index=0;index<MAX_LENGTH;++index) MyArray[index] = static_cast<unsigned int>(RandomGenerator(Random)); } int main(){ std::array<unsigned int,MAX_LENGTH> MyOrginalArray; RandomizeArray(MyOrginalArray); SortArray(MyOrginalArray,Compare); PrintArrayElements(MyOrginalArray); return 0x0000; } کد نوشته شده به زبان C : #include <stdio.h> #include <stdlib.h> #include <time.h> #include <stdbool.h> #define MAX_LENGTH 100000 bool Compare(unsigned int FirstVariable,unsigned int SecondVariable){ if(FirstVariable < SecondVariable) return true; return false; } void SortArray(unsigned int *MyArray,bool (*compare)(unsigned int,unsigned int)){ unsigned int tmp=0; for(unsigned int index=0;index < MAX_LENGTH;++index){ for(unsigned int AnotherIndex=0;AnotherIndex<MAX_LENGTH;++AnotherIndex){ if(compare(MyArray[index],MyArray[AnotherIndex])){ tmp = MyArray[index]; MyArray[index] = MyArray[AnotherIndex]; MyArray[AnotherIndex] = tmp; } } } } void RandomizeArray(unsigned int *MyArray){ srand(time(NULL)); for(unsigned int index=0;index < MAX_LENGTH ;++index) MyArray[index] = rand() % 1000; } void PrintArrayElements(unsigned int *MyArray){ for(unsigned int index=0;index<MAX_LENGTH;++index) fprintf(stdout,"%d\n",MyArray[index]); } int main(){ unsigned int Array[MAX_LENGTH]; RandomizeArray(Array); SortArray(Array,Compare); PrintArrayElements(Array); return 0x0000; }
  40. 1 امتیاز
    نام نیاز نیست خیلی طولانی باشه چون اگر اسکوپ بندی و استفاده از فضای نام و کلاس به جای خودش باشه به نظرم نام طولانی نیاز نیست میشه با حد اکثر سه کلمه منظور رو رسوند یا شایدم کمتر اگر از دییازنت پترن استفاده بشه و همه چیز اصولی باشه. ولی در کل نام باید معنی داشته باشه مخفف کردن هم اصلا کار قشنگی نیست مگر برای فرمت اطلاعات که کلا باید مخفف باشند.
  41. 1 امتیاز
    مرسی متشکرم. بله سرعت رو به شدت بالابرد ! اما ! این فلگ هایی که گفتید فقط فلگ اول O3- برای بهینه سازی هست که باعث افزایش سرعت میشه. و فلگ دوم ffast-math- به کامپایلر اجازه میده که یک سری از قوانین IEEE و ISO و نقض کنه : GCC Command Options و گفته شده که در صورت استفاده از فلگ ffast-math- از هیچ کدام سطح های بهینه سازی O- استفاده نکنید که باعث خروجی اشتباه میشه !
  42. 1 امتیاز
    عه سلام فکر میکردم راهنمایی کردم الان دیدم نکردم . ببین باید ابجکت جیسانت رو درست کنی بعد یه تابع داره برای تبدیل به استرینگ : QJsonObject jsonObj; // assume this has been populated with Json data QJsonDocument doc(jsonObj); QString strJson(doc.toJson(QJsonDocument::Compact)); بعد میتونید استرینگ رو به روش دلخواه بفرستید به کیو ام ال و اونجا پارسش کنید.
  43. 1 امتیاز
    با سلام در مورد دیتابیس باید خدمتتون عرض کنم بهتر هست کلاس کار با دیتابیس رو توی سی پلاس پلاس بنویسید و به صورت ماژول ازش استفاده کنید. الان مثالی نزدیک دستم نیست ولی کلیت به این صورت هست که شما یک کلاس برای کار با دیتابیس میسازید و توی کیو ام ل با اون کار می‌کنید. برای مثلا کلاس DatanaseManager رو می‌نویسید که تابع AddUser داره و ورودی فیلدهای ساختار داده کاربر رو می‌گیره و توی سی پلاس پلاس شما توی این تابع دیتابیس رو باز می‌کنید و اطلاعات ورودی رو ذخیره می‌کنید . بعد این کلاس DatanaseManager رو که از QObject به ارث برده توی کیو ام ال استفاده می‌کنید البته باید این رو بگم که دو روش برای استفاده هست که یک روش ریجستر کردن کلاس هست و اینطوری باید توی کیو ام ال ایمپورت بشه و ازش شی ساخته بشه و ... که این رو خیلی برای دیتابیس پیشنهاد نمی‌کنم چون اگر مثلا دیتابیستون اس کیو لایت باشه ممکنه چندین کانکشن به یک شی به وجود بیاد در لحظه و مشکلساز بشه و ایمپورت کردن و نمونه ساختن از دیتابیس توی هر فایل کیو ام ال هم اصلا جالب به نظر نمیاد. بهتره یک شی توی سی پلاس پلاس از کلاس دیتابیس درست کنید و اون رو با QQmlContext بفرستی به کیو ام ال و اینطوری توی تمام قسمت‌های QML در دسترس هست. اینم یه مثال خیلی کوچیک از فرستادن با QQmlContext در مورد باز شدن هم که جناب اسدزاده توضیح دادند یک روش ساخت آبجکت داینامیک هست با createComponent و روش دیگه هم همین هست که همه رو توی کامپوننت بنویسی و در زمان نیاز سورس لودرت رو ست کنی.
  44. 1 امتیاز
    خدارو شکر. خب وقتی سیستم عاملت رو تازه نصب کردی حتماً یک سری ابزار‌ها و کتابخانه‌ها روش وجود نداره. مثل کتابخانه‌های گرافیکی مورد نیاز، کامپایلر‌ و برخی از ابزار‌های دیگه که روی لینوکس باید این دو دستور رو اجرا کنی و نصبشون کنی. خطای lGL- هم مربوط به کتابخانه OpenGL هست.
  45. 1 امتیاز
    دقیقا من قبل این که این پیغام رو ببینم حل کردم مسله رو. اول این که مشکل من از Gradle بود که فایل باینری رو از سایت دانلود کردم و آدرس پوشه‌ی bin رو توی environment path اضافه کردم ظاهرا باید درست می شد، ولی نشد. بعد از این که این آدرس رو داخل Qt Creator توی بخش Build setting path وارد کردم حل شد.
  46. 1 امتیاز
    کتابخانه‌ی اوپن سی اِل مخفف Open Computing Language بستری برای برنامه‌هایی که قرار است بر سکو‌های ناهمگن یا تکیه بر پردازنده‌های مرکزی و پردازنده‌های گرافیکی و سایر پردازنده‌ها اجرا شوند. این کتابخانه دارای یک زبان بر پایه‌ی C99 و C++11 برای نوشتن کرنل‌ها و همچنین رابط‌های برنامه‌نویسی برای تعریف و پس از کنترل بستر استفاده شوند را دارا است. این کتابخانه چند‌پردازندگی را با استفاده از روش‌های وظیفه محور (Task-Based) و داده محور (Data-Based) پشتیبانی می‌کند. اوپن سی اِل توسط هر دو شرکت AMD/ATI و Nvidia پذیرفته شده است. در طراحی اوپن سی اِل، مقدار زیادی از رابطه‌های پردازشی با Cuda و رقیب آن، مایکروسافت دایرکت کامپیوت (Direct-Compute) به اشتراک گذاشته شده است. پیاده سازی‌های مربوطه از طرف Altea, AMD, Apple صورت گرفته شده است که در این میان OpenCL همراه با OpenGL به نفع Metal2 منسوخ شده اند. همچنین IBM, Imagination, Intel, Nvidia, Qualcomm, Samsung, Vivante, Xilinx و Ziilabs آن را پذیرفته‌اند. این کتابخانه یکی از قدرتمند ترین پلتفرم‌های موجود در بازار در مقابل DirectX می‌باشد گه از سوی کمیته‌ی Khronos Group اعلام شده است که پا به پای DirectX ه فعالیتش ادامه می‌دهد. جالب است بدانید نسخه‌های جدید این کتابخانه با قدرت بسیار زیادی بر روی PS4 و PS4 Pro استفاده می‌شوند. از آنجایی که PS4 از معماری GCN استفاده می‌کند، قابلیت پشتیبانی از DiectX 12 نیز برای آن فراهم شده است. بهتر است بدانید توسعه اصلی این پلتفرم توسط اپل انجام شده است که در حال حاضر توسط کمیته‌ی Khronos Group اداره می‌شود که بر روی طیف وسیعی از سخت افزار‌های روز و کارت گرافیکی‌های محتلف گرفته تا پردازنده‌های موجود پشتیبانی می‌شود. قدرت روز افزون اوپن سی اِل در حال افزایش است و شاید اگر قدرت و سرمایه‌ی مایکروسافت نبود، OpenCL سلطان بی چون و چرای بازار می‌شد. کودا با وجود انکار انویدیا، فریم ورکی است اختصاصی که در بسیاری از محصولات مورد استفاده قرار می‌گیرد. انویدیا تلاش می‌کند رقبایش را به استفاده از کودا وادار کند، اما تلاشش نتیجه بخش نبوده و می‌بینم که از این فریم ورک بیشتر در توسعه محصولات خودش استفاده می‌شود. از طرفی می‌توان ادعا کرد که Direct Compute هم اختصای است. چون فقط بر روی ویندوز و دایرکت اِکس ۱۱ به بالا اجرا می‌شود. اما OpenCL منبع باز است و بسیاری از شرکت‌های بزرک از جمله Nvidia از آن پشتیبانی می‌کنند. جهت نصب و راه اندازی این کتابخانه برای پردازنده‌های Intel به این بخش مراجعه کرده و نسخه‌ی Amd را در این بخش و برای Nvidia از این صفحه دریافت و استخراج نمایید. این کتابخانه در قالب SDK شامل include و lib تحت تو معماری x86 و x64 می‌باشد که باید در محیط توسعه‌ی نرم‌افزاری خود آن را معرفی کنید. در محیط Qt Creator طبق آموزش‌های قبل اقدام کنید. با توجه به اینکه ما SDK مربوطه را در مسیر C:/IntelOpenCL/sdk استخراج کرده‌ایم، کُد فایل .pro به صورت زیر خواهد بود: LIBS += -L$$PWD/../../../../../Intel/OpenCL/sdk/lib/x86/ -lOpenCL INCLUDEPATH += $$PWD/../../../../../Intel/OpenCL/sdk/include DEPENDPATH += $$PWD/../../../../../Intel/OpenCL/sdk/include جهت آزمایش عملکرد کتابخانه توجه داشته باشید که فایل‌های هدر در پلتفرم macOS در پوشه‌ی OpenCL و در محیط‌های ویندوز و لینوکس در مسیر CL موجود می‌باشند. #ifdef __APPLE__ #include <OpenCL/opencl.h> #else #include <CL/cl.hpp> #endif در ادامه کد زیر نتیجه‌ی جمع دو آرایه با یکدیگر را تحت OpenCL اجرا خواهد گرد: #include <iostream> #include <vector> #ifdef __APPLE__ #include <OpenCL/opencl.h> #else #include <CL/cl.hpp> #include <CL/opencl.h> #endif using namespace std; int main(){ //get all platforms (drivers) std::vector<cl::Platform> all_platforms; cl::Platform::get(&all_platforms); if(all_platforms.size()==0){ std::cout<<" No platforms found. Check OpenCL installation!\n"; exit(1); } cl::Platform default_platform=all_platforms[0]; std::cout << "Using platform: "<<default_platform.getInfo<CL_PLATFORM_NAME>()<<"\n"; //get default device of the default platform std::vector<cl::Device> all_devices; default_platform.getDevices(CL_DEVICE_TYPE_ALL, &all_devices); if(all_devices.size()==0){ std::cout<<" No devices found. Check OpenCL installation!\n"; exit(1); } cl::Device default_device=all_devices[0]; std::cout<< "Using device: "<<default_device.getInfo<CL_DEVICE_NAME>()<<"\n"; cl::Context context({default_device}); cl::Program::Sources sources; // kernel calculates for each element C=A+B std::string kernel_code= " void kernel simple_add(global const int* A, global const int* B, global int* C){ " " C[get_global_id(0)]=A[get_global_id(0)]+B[get_global_id(0)]; " " } "; sources.push_back({kernel_code.c_str(),kernel_code.length()}); cl::Program program(context,sources); if(program.build({default_device})!=CL_SUCCESS){ std::cout<<" Error building: "<<program.getBuildInfo<CL_PROGRAM_BUILD_LOG>(default_device)<<"\n"; exit(1); } // create buffers on the device cl::Buffer buffer_A(context,CL_MEM_READ_WRITE,sizeof(int)*10); cl::Buffer buffer_B(context,CL_MEM_READ_WRITE,sizeof(int)*10); cl::Buffer buffer_C(context,CL_MEM_READ_WRITE,sizeof(int)*10); int A[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; int B[] = {0, 1, 2, 0, 1, 2, 0, 1, 2, 0}; //create queue to which we will push commands for the device. cl::CommandQueue queue(context,default_device); //write arrays A and B to the device queue.enqueueWriteBuffer(buffer_A,CL_TRUE,0,sizeof(int)*10,A); queue.enqueueWriteBuffer(buffer_B,CL_TRUE,0,sizeof(int)*10,B); //run the kernel //alternative way to run the kernel cl::Kernel kernel_add=cl::Kernel(program,"simple_add"); kernel_add.setArg(0,buffer_A); kernel_add.setArg(1,buffer_B); kernel_add.setArg(2,buffer_C); queue.enqueueNDRangeKernel(kernel_add,cl::NullRange,cl::NDRange(10),cl::NullRange); queue.finish(); int C[10]; //read result C from the device to array C queue.enqueueReadBuffer(buffer_C,CL_TRUE,0,sizeof(int)*10,C); std::cout<<" result: \n"; for(int i=0;i<10;i++){ std::cout<<C[i]<<" "; } return 0; } نتیجه خروجی جمع دو آرایه با یکدیگر و همچنین شناسایی پلتفرم‌های قابل پشتیبانی در OpenCL: Using platform: Intel(R) OpenCL Using device: Intel(R) HD Graphics 4400 result: 02435768109
  47. 1 امتیاز
    گرافیک کامپیوتری همواره از جمله جذابترین جنبه های کامپیوترها بوده است. بازیها، برنامه های طراحی گرافیکی دو بعدی و سه بعدی و شبیه سازیها همگی به نوعی از قابلیتهای گرافیکی یک کامپیوتر بهره می‌برند. مهمترین نکته در این زمینه، برقراری تعادلی منطقی بین کیفیت تصاویر نمایش داده شده بر روی صحنه و سرعت اجرای برنامه می‌باشد. کتابخانه‌ی GLFW ابزاری برای استفاده با اوپن‌جی‌ال (OpenGL) است که امکاناتی برای برنامه‌نویس برای قابلیت کشیدن و مدیریت پنجره‌ها و کانتکست اوپن‌جی‌ال و ورودی جوس‌استیک، صفحه‌کلید و موشواره فراهم می‌کند. در این آموزش همانند کتابخانه‌های SDL و SFML نصب و راه اندازی جهت استفاده از OpenGL را توضیح داده‌ایم. جهت دریافت کتابخانه‌ی GLFW آن را از این بخش دریافت کنید. فایل دانلود شده را در مسیر C:/glfw-3.2.1.bin.WIN64 استخراج کنید. طبق آموزش‌های مرتبط با نحوه‌ی افزودن کتابخانه به محیط توسعه اقدامات لازم را انجام دهید. در صورتی که می‌خواهید کتابخانه را به صورت سفارشی کامپایل کنید، طبق آموزش‌هایی که از قبل ارائه شده است اقدام کنید. نکات مرتبط با نصب و راه اندازی در بخش Additional Dependencies در محیط Visual Studio گزینه‌های مورد نیاز به صورت زیر خواهند بود: glfw3.lib; opengl32.lib; User32.lib; Gdi32.lib;Shell32.lib در محیط Qt Creator گزینه‌های -lglfw3 -lopengl32 -lUser32 -lGdi32 -lShell32 مورد نیاز هستند. win32: LIBS += -L$$PWD/../../../../glfw-3.2.1.bin.WIN64/lib-vc2015/ -lglfw3 -lopengl32 -lUser32 -lGdi32 -lShell32 INCLUDEPATH += $$PWD/../../../../glfw-3.2.1.bin.WIN64/include DEPENDPATH += $$PWD/../../../../glfw-3.2.1.bin.WIN64/include کُد زیر نمونه مثالی از OpenGL تحت glfw می‌باشد که بعد از کامپایل و اجرا پنجره‌ی خالی تولید شده را تولید می‌کند: #include <GLFW/glfw3.h> int main(void) { GLFWwindow* window; /* Initialize the library */ if (!glfwInit()) return -1; /* Create a windowed mode window and its OpenGL context */ window = glfwCreateWindow(640, 480, "Hello World", NULL, NULL); if (!window) { glfwTerminate(); return -1; } /* Make the window's context current */ glfwMakeContextCurrent(window); /* Loop until the user closes the window */ while (!glfwWindowShouldClose(window)) { /* Render here */ glClear(GL_COLOR_BUFFER_BIT); /* Swap front and back buffers */ glfwSwapBuffers(window); /* Poll for and process events */ glfwPollEvents(); } glfwTerminate(); return 0; } جهت بررسی صحت کارکرد تحت کد دستوری زیر مثلثی را در مرکز پنجره تولید می‌کنیم: glBegin(GL_TRIANGLES); glVertex2d(-0.5f, -0.5f); glVertex2d(0.0f, 0.5f); glVertex2d(0.5f, -0.5f); glEnd(); در نهایت نتیجه کُد خروجی به صورت زیر خواهد بود: آموزش نصب و راه اندازی کتابخانه در ++C
  48. 1 امتیاز
    این مثال با پای کیوت هست ولی سخت نیست تبدیلش به نمونه سی پلاس پلاسش میتونی از روی همین یه مثال سی پلاس پلاس برای خودت بنویسی و کارت رو راه بندازی مثال سرچ در لیست ویو کیو ام ال
  49. 1 امتیاز

    نگارش 1.9.0

    6 دریافت

    این کتابخانه توسط شرکت appinf تولید و انتشار یافته است. هدف از این کتابخانه در مرحله اول فراهم نمودن کلاس های قدرتمند و تخصصی برای شبکه و اینترنت می‌باشد.

    رایگان

  50. 1 امتیاز
    با سلام و احترام، دوستان از اینکه بستری برای دوره هم بودن جامعه برنامه نویسان استارتاپی ایران ایجاد شده بسیار خوشحالیم! امیدواریم که وقتی این متن رو می‌خونین در نبرد همیشگی روزانه‌تون با باگ‌ها پیروز میدان بوده باشین. لطفا قبل از شروع، یه فنجان دمنوش، چایی یا قهوه بردارین و بعد ادامه‌ی متن رو بخونین. بله جدی گفتیم… همین الان برین اینکار رو بکنین… ما همین جا منتظرتون هستیم [چند دقیقه بعد] سلام دوباره. نوشیدنی خوبی به نظر میاد، بوش حتی از پشت اسکرین هم اینجا می‌رسه اگه تازه به عنوان مهمان وارد مرجع شدین می‌تونید در صورت تمایل ثبت نام کنید. دقت کنید که ثبت نام برای همگان رایگان هست اما بر اساس قوانین ذکر شده دسترسی‌ها به صورت کامل برای گروه‌های پیش فرض ثبت نام شده آزاد نیست. توجه داشته باشید که، برای دسترسی به گروه‌های برتر باید فعالیت‌های شما طبق شرایط ذکر شده تایید شود. در مرجع معرفی نام و نام خانوادگی بسیار مهم است! چرا که در معتبر سازی شما به عنوان یک متخصص قابل اعتماد بسیار مهم است. بنابراین بد نیست خودتون رو در بخش درباره من معرفی و نام نمایشی کاربری خودتون رو به صورت واقعی وارد کنید. #قوانین و شرایط عضویت و فعالیت
این صفحه از پرچمداران بر اساس منطقه زمانی تهران/GMT+03:30 می باشد
×
×
  • جدید...