C++
Відкрити в Telegram
گروه آموزش زبان برنامه نویسی سی، سی پلاس پلاس خدمت شما دوستداران گرامی با امید بهره بردن و موفقیت شما همراهان🌹 ممنون که همراه ما میشین و ما رو حمایت میکنین 🌹 https://t.me/ourcpp
Показати більше2 130
Підписники
-224 години
-57 днів
-4730 день
Архів дописів
2 130
سلام
لینک گروه سی ، سی پلاس پلاس :
👇👇👇👇
https://t.me/ourcpp
لینک گروه سی شارپ :
👇👇👇👇
https://t.me/ourcsharpp
لینک گروه پایتون :
👇👇👇👇
https://t.me/pygroupp
گروه چت :
👇👇👇👇
https://t.me/engineeringcomputerr
👨💻👩💻
2 130
سلام
وقت تون بخیر
پیجی رو میخوام بهتون معرفی کنم که تازه شروع به فعالیت کردند .
پست و استوری های جالبی گذاشتن .
اگه دوست داشتید با فالو کردن حمایتشون کنید .
https://www.instagram.com/p/CbjmoUSNcdh/?utm_medium=copy_link
متشکر
🌹🌹
2 130
سلام
وقت تون بخیر
پیجی رو میخوام بهتون معرفی کنم که تازه شروع به فعالیت کردند .
پست و استوری های جالبی گذاشتن .
لطفا با فالو کردن حمایتشون کنید .
https://www.instagram.com/p/CbjmoUSNcdh/?utm_medium=copy_link
متشکر
🌹🌹
2 130
همه به شما می گویند:
سال خوبی داشته باشید
ولی من به شما می گویم :
•••سال خوبی را برای خودتان خلق کنید•••
به فکر آمدن روزهای خوب نباشید!
آنها نخواهند آمد …
به فکر ساختن باشید…
روزهای خوب را باید ساخت
آرزو می کنم
بهترین برنامه نویس سال جدید باشید …
پیشاپیش نوروز مبارك
🌹🌹
2 130
میلاد با سعادت سلطان عشق حضرت سید الشهدا (ع) و روز پاسدار
امیر عشق حضرت عباس بن علی (ع) و روز جانباز
پور عشق حضرت سیدالساجدین بر تمام محبان مبارک باد
🌹🌹
2 130
سائل درمانده نا امید نگردد، گر که بکوبد در سرای محمد (ص)
ای که ندای اذان رسید به گوشت
هان که به گوشت رسد ندای محمد (ص)
مبعث رسول اکرم بر همه مسلمانان جهان مبارک باد.
🌹🌹
👩💻🧑💻
2 130
if consteval:
همانطور که در بخش قبلی گفتیم، کمیته استاندارد شکل جدیدی از دستور if را پیشنویس کرده که سعی در حل مشکلات std::is_constant_evaluated و consteval دارد:
if consteval { }
اگر ارزیابی این عبارت در حین constant evaluation اتفاق بیافتد، اولین substatement اجرا میشود. در غیر اینصورت، substatement دوم (در صورت وجود) اجرا میشود.
در واقع if consteval دقیقا مثل:
if (std::is_constant_evaluated()) { }
رفتار میکند، با سه تفاوت:
1. دیگر نیازی به header include نیست.
2. سینتکس متفاوتی دارد، که بهطور کامل ما را از سردرگمی در مورد روش مناسب برای بررسی اینکه آیا در constant evaluation هستیم یا خیر، دور میکند.
3. میتوانیم از if consteval برای فراخوانی immediate functionها استفاده کنیم.
برای توضیحِ بیشترِ نکته آخر: قوانین فعلی، به شما اجازه میدهد که یک تابع consteval را از داخل تابع consteval دیگر فراخوانی کنید. ما میتوانیم این کار را با این ساختار انجام دهیم:
An expression or conversion is in an immediate function context if it is potentially evaluated and its innermost non-block scope is a function parameter scope of an immediate function. An expression or conversion is an immediate invocation if it is an explicit or implicit invocation of an immediate function and is not in an immediate function context. An immediate invocation shall be a constant expression.
Source: [expr.const]/13
با توسعهدادنِ immediate function context برای شاملشدنِ بلوک if consteval، میتوانیم به همان مثال قبلیمان (مثال پست قبل) اجازه کار بدهیم:
consteval int f(int i) { return i; }
constexpr int g(int i) {
if consteval {
return f(i) + 1; // ok: immediate function context
}
else {
return 42;
}
}
consteval int h(int i) {
return f(i) + 1; // ok: immediate function context
}
علاوه بر این، چنین ویژگیای، امکان پیادهسازی آسان std::is_constant_evaluated را فراهم میکند:
constexpr bool is_constant_evaluated() {
if consteval {
return true;
}
else {
return false;
}
}
به همین سادگی!
برای مشاهده ادامه مطلب به بلاگ مراجعه کنید.
#Cpp23 #Consteval #If_Consteval2 130
میتوانی پزشک باشی و زندگیها را نجات دهی
میتوانی حقوقدان باشی و از حقی دفاع کنی
میتوانی سرباز باشی و از زندگی بسیاری دفاع کنی
و میتوانی مهندس باشی و همه این کارها را با هم انجام دهی.
💫روز مهندس مبارک💫
🧑💻👩💻
2 130
مشکلات
consteval و std::is_constant_evaluated:
C++20 چندین ویژگی جدید برای کمک به برنامهنویسان در پرداختن به constant evaluationها داشت. دو مورد از آنها std::is_constant_evaluated و consteval هستند، که هر دو در 2018 به تصویب رسیدند. consteval برای توابعی است که فقط در طول ارزیابی constant میتوانند فراخوانی شوند. is_constant_evaluated یک تابع جادویی برای بررسی اینکه آیا evaluationئه فعلی constant evaluation است یا خیر استفاده میشود تا برای مثال، پیادهسازی معتبری از یک الگوریتم برای زمان constant evaluation و اجرای بهتر برای Run-time ارائه دهد.
با این حال، این ویژگی ها تعامل ضعیفی با یکدیگر دارند و مسائل دیگری دارند که توسعهدهندگان را سردرگم میکند که در ادامه به آنها خواهیم پرداخت.
نکته: ما در این مقاله قصد نداریم شما را با consteval و std::is_constant_evaluated آشنا کنیم، بلکه تنها به مشکلات آنها خواهیم پرداخت.
برای آشنایی اولیه با این موارد، از گوگل کمک بگیرید.
دو مشکل وجود دارد که این مقاله می خواهد به آن بپردازد:
1. تعامل بین constexpr و consteval:
اولین مشکل تعامل بین آن تابع جادویی و consteval جدید است. به مثال توجه کنید:
constexpr int g(int i) {
if (std::is_constant_evaluated()) {
return f(i) + 1; // <==
} else {
return 42;
}
}
consteval int h(int i) {
return f(i) + 1;
}
تابع h در اینجا اساساً یک نسخه lift شده و با ارزیابی ثابت از تابع g است. در زمانِ ارزیابی ثابت هم همین کار را میکنند، با این تفاوت که در Run-time نمیتوانید h را فراخوانی کنید و g این مسیر اضافی را دارد. شاید این کد فقط با h شروع شده باشد و شخصی تصمیم بگیرد که نسخه رانتایم را مفید بداند و آن را به g تبدیل کند.
متأسفانه h خوب شکل گرفته در حالی که g بد فرم است. شما نمیتوانید آن فراخوانی را با f (که در کد با یک فلش مشخص شده است) در آن مکان برقرار کنید. حتی اگر این فراخوانی تنها در طول constant evaluation اتفاق بیافتد، کافی و بهدردبخور نیست.
با یکسری شرایط خاص، فراخوانی f در داخل g یک فراخوانی Immediate است و باید یک constant expression باشد و اینطور نیست (توضیح: در C++20، یک تابع Immediate تابعی است که در آن هر فراخوانی تابعی بهطور مستقیم یا غیرمستقیم یک compile-time constant expression تولید میکند.)
این توابع (immediate) با استفاده از یک کلمه کلیدی consteval قبل از نوع برگشتی خود اعلام می شوند. در حالی که فراخوانی f در داخل h یک immediate invocation در نظر گرفته نمیشود زیرا در یک context از تابع immediate قرار دارد (یعنی از یک تابع immediate دیگر فراخوانی شده است)، بنابراین دارای مجموعه ضعیفتری از محدودیتها است که باید از آنها پیروی کند.
به عبارت دیگر، این نوع ساختارِ فراخوانیِ شرطیِ تابعِ consteval از یک تابع constexpr کار نمیکند.
ما این عدم ترکیبپذیری ویژگیها را مشکلساز می دانیم و فکر میکنیم که میتوان آن را بهبود بخشید.
برای خواندن ادامهی مطلب به بلاگ مراجعه کنید...
#Cpp20 #Cpp23 #Consteval #Constant_Evaluated2 130
سلام دوستان عزیز
ممنونم از عزیزانی که در کانال پست میذارند .
هدف این کانال : آموزش و استفاده از تجربه دوستان .
ممنون از همگی که کانال و گروه رو همراهی میکنید .
برای شادی تون ، یه خسته نباشید به همگی
و دوباره انرژی گرفتن .
پخش اجرای انپلاگد جناب شادمهر
👇👇👇👇
https://youtu.be/a8PoClPoCn4
2 130
ناگهان یک صبح زیبا آسمان گل کرده بود
خاک تا هفت آسمان، بغض تغزل کرده بود
حتم دارم در شب میلادت، ای غوغاترین!
حضرت حق نیز در کارش تأمل کرده بود
هر فرشته، تا بیایی، ای معمایی ترین!
بال های خویش را دست توسل کرده بود
ولادت حضرت علی (ع) ( روز مرد ، پدر ) مبارک باد.
🧑💻👨💻
2 130
Modules
بخش سوم:
فضای نام:
همانطور که قبلاً ذکر شد، فضاهای نام ++C متعامد با ماژولها هستند. اعتراف میکنم که این در ابتدا باعث سردرگمی من شد. نه به اندازه نحو، فلسفه عمومی استفاده از فضاهای نام و ماژول ها، جایی که هر کدام در یک ساختار معماری قرار میگیرند میتواند گیجکننده باشد.
از منظر عملی، فضاهای نام مانند قبل رفتار میکنند، بنابراین هیچ تغییر واقعی در استفاده از آنها وجود ندارد، به عنوان مثال:
func.cxx
export module mod;
namespace X {
export void func();
export void func(int);
}
func_impl.cxx
module;
#include <iostream>
module mod;
namespace X {
void func() {
std::cout << "hello, world!\n";
}
void func(int p) {
std::cout << "hello, " << p << '\n';
}
}
main.cpp
import mod;
int main(){
X::func();
X::func(42);
}
Export کردنِ فضای نام:
اگر فضای نامی را Export کنیم، تمام declarationهای داخل آن فضای نام بهطور خودکار در اینترفیسِ ماژول گنجانده میشوند، بهعنوان مثال:
func.cxx
export module mod;
export namespace X {
void func();
void func(int);
}
Export کردنِ Typeها و...:
هرچیزی که بهعنوان بخشی از اینترفیس ماژول نیاز داریم، باید Export شود. بهعنوان مثال، اگر تابع یک object reference را بهعنوان پارامتر بگیرد، قوانین type definition visibility معمولی بر روی آن اعمال میشود. مثال را در Gist ببینید.
مجموعه ای از قوانین و Exceptionها در مورد Export کردنِ آیتمها، مانند templateها و ارزیابی ADL وجود دارد. من قصد ندارم وارد این موارد در اینجا شوم زیرا بسیار use-specific است. شاید بعدا در پستی دیگر، بهصورت اختصاصی به این موضوع پرداختیم.
Includeها:
در مثال بالا، میبینید که از دستورالعمل پیشپردازندهی سنتیِ include# برای گنجاندن هدر کتابخانه استاندارد iostream استفاده کردهایم. استاندارد موارد زیر را مجاز میکند:
import <iostream>;
import "header.h";
این توسط GCC11 پشتیبانی میشود، اما برخی فیلترها وجود دارد که باید از آنها عبور کنید تا ابتدا یک هدر تعریفشده توسط کاربر قابل Export باشد (به دستورالعملِ
-fmodule-headerمراجعه کنید). اگر یک فایل header-only دارید، مثل: header.h
#ifndef _HEADER_
#define _HEADER_
constexpr int life = 42;
#endif
و میخواهید آنرا import کنید باید اول آن را کامپایل کنید:
$ g++ -c -std=c++20 -fmodule-header header.h
این یک فایلِ header.h.gcm تولید میکند. اکنون میتوانید هدر را import کنید:
import "header.h";به Semicolon دقت کنید! بهعلاوه، مایکروسافت قبلا کتابخانه استاندارد را در یک ساختار ماژول قرار داده است، بنابراین در MSVC میتوانید از
std.core استفاده کنید.
import std.core
در پستهای بعدی، در مطالبی پیچیدهتر، تواناییِ تقسیم اینترفیس یک ماژول به چندین فایل (به نام پارتیشن) را پوشش خواهیم داد. با استفاده از پارتیشنها، سطح ظاهری ساده به نظر میرسد، اما این صرفا یک خیال واهی است! 😄
با ما همراه باشید.
#Cpp20 #Module
@CppFarsi2 130
سلام
لینک گروه سی ، سی پلاس پلاس :
👇👇👇👇
https://t.me/ourcpp
لینک گروه سی شارپ :
👇👇👇👇
https://t.me/ourcsharpp
لینک گروه پایتون :
👇👇👇👇
https://t.me/pygroupp
گروه چت :
👇👇👇👇
https://t.me/engineeringcomputerr
کانال پروژه و تمرین :
👇👇👇👇
https://t.me/projecpppy
2 130
بنابراین، این اولین تغییر مهم را به ما میدهد:
در ماژول ها، تمام اعلانها و تعاریف، private هستند مگر اینکه export شوند.
اما صبر کنید، دیگر Linker فقط یک Linker داخلی نیست! حالا بهطور رسمی با Module Linker مواجهیم و کاملا با Linker داخلی متفاوت است. این تفاوت زمانی آشکار میشود از یک ماژول و پارتیشنهای چندفایلی استفاده کنید (بعدا در پستی جداگانه به این موضوع خواهیم پرداخت.)
برای رفع این مشکل، تابع را export میکنیم:
func.cxx
module;
#include <iostream>
export module mod;
export void func() {
std::cout << "hello, world!\n";
}
اکنون پروژه با موفقیت کامپایل و Link میشود:
$ g++ -c -std=c++20 -fmodules-ts func.cxx
$ g++ -c -std=c++20 -fmodules-ts main.cpp
$ g++ main.o func.o -o App
$ ./App
hello, world!
جداسازی فایلهای اینترفیس و پیادهسازی:
در برخی مواقع، ممکن است ماژول خود را به چندین فایل تقسیم کنیم تا بتوانیم آن را مدیریت کنیم و به زمانِ rebuild شدنِ پروژه کمک کنیم.
همانطور که گفتیم، هر فایل مربوط به یک ماژول، Module Unit نامیده می شود. ما دو unit ایجاد میکنیم:
یک واحد رابط ماژول اولیه یا همان PMIU (Primary Module Interface Unit).
یک واحد پیادهسازی ماژول
Primary Module Interface Unit:
هر ماژول نامگذاری شده تنها و تنها باید یک واحد رابط ماژول اولیه داشته باشد. این فایل اصلاح شده func.cxx است که حاوی این عبارت است:
export module MODULE-NAME;
مثال:
export module mod; export void func();این تمام چیزی است که ما نیاز داریم؛ ما یک ماژول را نامگذاری کردهایم و حالا میتوانیم مثل گذشته آن را کامپایل کنیم. Module Implementation Unit: در حال حاضر هیچ تبدیل نامگذاری اصطلاحیای وجود ندارد، بنابراین من با func_impl.cxx پیش میروم. شما نمی توانید از func.ixx استفاده کنید زیرا یک آبجکت فایل func.o را نیز ایجاد میکند که آبجکت فایل ایجاد شده توسط func.cxx را بازنویسی میکند. یک واحد پیادهسازی شامل خط زیر است:
module MODULE-NAME;
این امر به طور ضمنی هر چیزی را که در PMIU تعریف شده را در بخش پیادهسازی در دسترس قرار میدهد (برعکس این موضوع صادق نیست). توجه داشته باشید که بخشهای پیادهسازی نمیتوانند export داشته باشند.
func_impl.cxx
module;
#include <iostream>
module mod;
void func() {
std::cout << "hello, world!\n";
}
حالا میتوانیم این واحد پیادهسازی را کامپایل کنیم:
$ g++ -c -std=c++20 -fmodules-ts func_impl.cxx
که func_impl.o را ایجاد میکند - ما دو آبجکت فایل ماژول را به عنوان بخشی از linkage خود داریم، البته، این بدان معناست که تغییرات در پیادهسازی ماژول نیازی به کامپایل مجدد از سوی کلاینتها ندارد (تقلید از رفتار هدرهای موجود).
$ g++ main.o func.o func_impl.o -o App
$ ./App
hello, world!
با GCC، بخش اینترفیس باید قبل از بخش پیادهسازی کامپایل شود.
export:
تا کنون، ما فقط یک تابع را export کردهایم. با فرض اینکه چندین تابع برای export داریم، استاندارد با استفاده از کلمهکلیدی export این امکان را به ما میدهد.
func.cxx
export module mod; export void func(); export void func(int);بلوک Export: از طرف دیگر، بهسادگی میتوانیم بسیاری از declarationها را در یک بلوک
export گروهبندی کنیم، مثال:
export module mod;
export {
void func();
void func(int);
}
در بخش بعدی به فضای نامها و export کردن آنها در ماژولها خواهیم پرداخت.
#Cpp20 #Module2 130
Modules
بخش دوم:
در C++20، به هر فایلی که حاوی سینتکس ماژول باشد، Module Unit گفته میشود. بنابراین یک ماژول نامگذاری شده ممکن است از یک یا چند Module Unit تشکیل شده باشد.
ماژول تک فایلی (کامل):
بخش اول: کد Pre-C++20:
بیایید با Hello World شروع کنیم.
خب از تقسیم رفتار در دو فایل آغاز میکنیم:
ما دو فایل func.cpp و main.cpp داریم:
func.cpp
#include <iostream>
void func() { // definition
std::cout << "Hello, World!\n";
}
main.cpp
void func(); // declaration
int main() {
func();
}
ما میتوانیم این برنامه را build و run کنیم:
$ g++ -c func.cpp
$ g++ -c main.cpp
$ g++ -o App main.o func.o
$ ./App
Hello, World!
در این برنامه چون تابع func، بهطور پیشفرض external linkage دارد، با موفقیت Build میشود.
بنابراین تا زمانی که main یک declaration معتبر داشته باشد، فایل main.cpp میتواند کامپایل شود و linker سمبلهای imported / exported را مدیریت کند.
بخش دوم: ماژول C++20:
در پروپوزال P1103R3 بود که پیشنهادات اولیه برای پشتیبانی از ماژولها به این شکل صادر شد:
A complete module can be defined in a single source file.
ابتدا ما باید ماژول نامگذاری شدهی خودمان را ایجاد کنیم. یک فایل ماژول کامل، معمولا دارای دو، یا احتمالا سه بخش (به نام fragments) خواهد بود:
1. یک global module fragment - جاییست که چیزهایی را که نیاز داریم اضافه میکنیم (اختیاری).
2. محدوده اصلی ماژول - جاییست که میتوانیم typeها و رفتارها را export کنیم.
3. یک fragment اختصاصی - این بخش به قسمت اینترفیس ماژول که میتواند بر رفتارِ سایر translation unitها تاثیر بگذارد، پایان میدهد. (اختیاری).
نکته: fragment خصوصی فقط میتواند در ماژولهای تکفایلی ظاهر شود. نسخه فعلی GCC یعنی 11.2 از fragmentهای private پشتیبانی نمیکند. اما ما از آنها در این پست، چشمپوشی نمیکنیم.
استاندارد ++C پسوند فایل را تعریف نمیکند. این صرفا یک toolchain specific است. در GCC، پسوند نام فایل، نحوه برخورد با فایل را برای هر فایل ورودی مشخصی تعیین میکند. GCC پسوندهای فایل زیر را به عنوان Source کد ++C تفسیر میکند که باید از قبل پردازش شوند:
file.cc
file.cp
file.cxx
file.cpp
file.c++
file.C
ما همیشه پسوند cpp. را برای فایلهای سورس ++C در پروژههایمان و هنگام آموزش ++C ترجیح میدهیم. بنابراین برای این منظور، در مثالهای زیر از cpp. برای فایلهای سورس معمولی ++C، و از cxx برای فایلهای ماژول استفاده خواهم کرد. این صرفا یک ترجیح شخصیاست.
قابل ذکر است، مایکروسافت استفاده از پسوند .ixx را برای رابطهای ماژول انتخاب کرده است. ما میتوانیم از file.ixx استفاده کنیم، اما در GCC، باید از این دستور:
-x c++ file.ixxاستفاده کنیم تا مشخص کنیم که فایل باید بهعنوان یک فایل ++C در نظر گرفته شود. به جای اینکه لقمه را دور سرمان بچرخانیم، از همان
cxx. استفاده میکنیم، به این معنی که GCC آن را به عنوان یک فایل ++C استاندارد در نظر بگیرد.
برای تبدیل فایل اصلی func.cpp به یک ماژول (func.cxx)، این خط را اضافه میکنیم:
export module MODULE-NAME;
برای مثال:
func.cxx
#include <iostream>
export module mod;
void func() {
std::cout << "hello, world!\n";
}
با این حال، این کد کامپایل نخواهد شد. عبارت include باید در Global Fragment باشد. Global Fragment باید مقدم بر main purview باشد و بهسادگی با کلمهکلیدی module معرفی شود، مثال:
func.cxx
module;
#include <iostream>
export module mod;
void func() {
std::cout << "hello, world!\n";
}
حالا میتوانیم این ماژول را main اضافه کنیم:
main.cpp
import mod;
int main(){
func();
}
حالا میتوانیم func.cxx را کامپایل کنیم:
$ g++ -c -std=c++20 -fmodules-ts func.cxx
توجه داشته باشید، در GCC C++20، ماژولها در حال حاضر تنها با مشخص کردن c++20 فعال نمیشوند. شما همچنین باید دستورالعمل -fmodules-ts را بنویسید.
خب، همانطور که انتظار میرود، کامپایلر یک آبجکت فایلِ func.o ایجاد میکند. با این حال، همچنین متوجه خواهید شد که یک زیرشاخه به نام gcm.cache با فایل mod.gcm ایجاد شده است. این فایل رابط ماژول تولید شده توسط کامپایلر است.
اگر پیش برویم و main.cpp را کامپایل کنیم:
$ g++ -c -std=c++20 -fmodules-ts main.cpp
> main.cpp: In function 'int main()':
main.cpp:5:5: error: 'func' was not declared in this scope
5 | func();
| ^~~~
ما این خطا را دریافت میکنیم که func تعریف نشده است. اگر بخواهیم آن را در main.cpp اعلام کنیم (مانند قبل) ساخته میشود اما در link شدن fail خواهد شد.2 130
Modules
بخش اول:
یکی از تغییرات بزرگ در استاندارد C++20، گنجاندن ماژولها است. ماژولها قرار است ساختار Code baseهای ++C را بهطور قابلتوجهی تغییر دهند و احتمالا مرگ نهایی Headerها را نشان دهند (اما نه در طول زندگی من.). همچنین راه را برای داشتن یک سیستم Package manager یکپارچه شبیه به Cargo package manager در Rust باز میکند. اگرچه من احساس میکنم استاندارد کردن یک Build system یکپارچه، یک نبرد خونین است!
خب بیاید شر درست کنیم! 😂 اگر میخواهید یک بحث داغ در هر انجمن/ کانال / گروه ++Cای شروع کنید، فقط بیان کنید که یک Build system خاص (مثل CMake- Bazel - Meson و...) بهتر از سایرین است. یا اینکه بگویید فقط یکی از آنها خوب است و بقیه آشغال هستند! ببینید چه آشوبی بهپا خواهد شد.
اما چرا این را گفتم؟ ادامه مطلب را مطالعه کنید تا چالشها را درک کنید.
برگردیم سراغ ماژولها.
خب اول از همه این را بگویم که با "چیستی" رابطه چندان خوبی ندارم و همیشه دنبال "چرایی" بودهام. سایتها و مقالات زیادی دربارهی چیستیِ ماژولها در ++C حرف زدهاند، مانند مستندات مایکروسافت. اما بهنظر من اکثر این مقالات و مستندات، "چرایی" را از دست دادهاند و صرفا دنبالِ "چیستی"اند.
اول از همه، هنگام بحث در مورد ماژولها، مطمئنا باید در مورد "ماژولار بودن" صحبت کنیم. ما قبلا یک شکل از ماژولاریتی در ++C با مدلِ object / class داشتیم.
اما این ماژولاریتی در ابعاد کوچکی بود.
ماژولها به "ماژولاریتی در ابعاد بزرگ" میپردازند، یعنی ماژولار بودن در سطح برنامه!
بنابراین ما با افزودن ماژول ها سعی در حل چه مشکلی داریم؟
بیایید صادق باشیم، "هدرها یک آشفتگی هستند" - بله آنها میتوانند به طور موثر استفاده شوند، اما اغلب، من هدرهای بسیار ضعیفی را میبینم. یک برنامه کاربردی که به خوبی ساخته شده است، معمولاً دارای جفت فایل برای میمیک یک ماژول است، مانند file.h و file.cpp. اما هیچ اجباری برای این رویکرد وجود ندارد. ما همچنین باید قوانین ارتباط خارجی و داخلی را برای ایجاد ایمن معماری ماژولار درک کنیم.
مشکل اصلی headerها این است که آنها فقط تا قبل تا پردازش وجود دارند. درواقع headerها در فاز compilation وجود ندارند!
ما میتوانیم یک برنامه کامل با ++C بدون header بنویسیم! اما در این صورت کدهای تکراری (declarations) زیادی وجود خواهد داشت، و این یک کابوس برای تعمیر و نگهداری کد خواهد بود.
ماژولاریتی در ابعاد بزرگ، معمولا با معماری و ساخت برنامه (فایلهایی که build ما را تشکیل میدهند) ارتباط نزدیکی دارد.
قبل از ادامه لازم است تاکید کنم که ماژولها و فضاهای نام، کاملا متفاوت هستند و به عنوان جنبههای مستقل از هم، وجود دارند (در بخش بعدی توضیح خواهم داد.).
زبان های دیگر:
بسیاری از زبانهای مدرن تمایل دارند که معنای یک ماژول را حولِ تمام کدهای ماژولِ موجود در یک فایل واحد، build کنند، به عنوان مثال: جاوا و پایتون.
آنها مفاهیم import و export تایپها و رفتارها از ماژولهای دیگر را دارند. این بسیار شبیه به معنای عمومی/خصوصی کلاس است اما در محدودهی فایل.
جالب اینجاست که زبانهای قدیمیتر مانند Ada و Modula-2 هم که در دهه 1980 طراحی شدهاند (تقریباً همزمان با ++C اصلی) از ساختارِ دو فایلی برای تعریف ماژولها (یا پکیجها در مورد Ada) استفاده میکردند. این طراحیها رابطِ ماژول را از پیادهسازی جدا میکند.
مزایای قابل توجه رابط / پیادهسازی file structure:
- بهبود build time
- ادغام و آزمایش سادهتر
البته، این یکی دیگر از موضوعات داغ بحث است.
ساختار فایل ماژول C++20:
به جرات میتوان گفت که حالا ++C، دارد ++C میشود! به جای روش ساده ساختاربندی مدل، ما رویکرد swiss-army-knife برای module construction به ما داده شدهاست. راههای زیادی برای انجام همین کار و موارد خاص وجود دارد. این مساله در ابتدا باعث ایجاد مشکلات زیادی برای من شد چون مدل ذهنی من (بر پایه الگوهای زبانهای دیگر) با چیزی که به آن معرفی شد همسو نبود.
در واقع در ابتدا میگفتم: هیچ راهی برای استفاده صحیح از ماژول های C++20 وجود ندارد!
مطمئن هستم که با گذشت زمان، اصطلاحات جدیدی در مورد استفاده از ماژولها خواهیم داشت، اما در حال حاضر میتوانم سه مدل واضح از ماژولها را ببینم (قانون 80:20 را در نظر بگیرید.):
1- ماژول تکفایل - مدل جاوا / پایتونی یا یک ماژول کامل.
2- یک فایل interface جداگانه و فایل پیادهسازی برای یک ماژول - مدل Ada.
3- ترکیب چندین فایل جداگانه برای تعریف یک مفهوم واحد - مدل C++20.
در بخش بعدی بهصورت عملی به Moduleها خواهیم پرداخت.
#Cpp20 #Module
2 130
تعریف کردن function templateها در ++C همیشه پیچیده و طولانی و در عین حال عجیب و جذاب بوده.
حالا در C++20 استاندارد روش جدیدی برای تعریف function templateها معرفی کرده که مختصرتر و سازگارتر با Lambdaها است به نام:
Abbreviated function templates
یا همان الگوهای تابع مخفف.
در این پست میخواهیم نحوه استفاده از این function templateها بههمراه نحوه اعمال آنها در C++20 concepts ها را بررسی کنیم.
در C++11 لامبداها معرفی شدند که به این شکل بودند:
[captures] (type_1 param_1, type_2 param_2) { body(param_1, param_2); }
شما فقط میتوانید این لامبدا را با آرگومانهای type_1 و type_2 فراخوانی کنید.
با اینحال، ما اغلب از لامبدا در موقعیتهایی استفاده میکنیم که املای کاملِ انواع آن دشوار است (مخصوصا موقع استفاده از ویژگیهایی مثل ranges).
حالا C++14 به شما این امکان را میداد که لامبداهایی بسازید که با استفاده از کلمهکلیدی auto میتوانید با هر نوع آرگومانی آن را فراخوانی کنید:
[captures] (auto param_1, auto param_2) { body(param_1, param_2); }
اکنون میتوانید هر Typeای را به عنوان آرگومان ارسال کنید. abbreviated function templateها در C++20 به شما این امکان را میدهد که این نوع syntax را در function templateها اعمال کنید.
در C++17 ممکن است بخواهیم تابعی بنویسیم که بهعنوان یک Function template، حیوانات را سرشماری کند، بنابراین میتوانیم کاری کنیم آن تابع با هر نوع حیوانی فراخوانی شود:
template <class Animal>
void animal_counts(Animal const& the_animal);
در C++20 میتوانید این کار را بهسادگی با auto انجام دهید:
void animal_counts(auto const& the_animal);
این نسخه کوتاهتر است، نیاز به نامگذاریهای کمتری دارد و با C++14 Lambdas سازگارتر است.
با اینحال، الگوی تابع بالا مشکلی دارد: طبق تعریف، ما میتوانیم به معنای واقعی کلمه، هرچیزی را به فانکشن پاس بدهیم، در واقع ما میتوانیم فراخوانیهایی به این شکل داشته باشیم:
animal_counts(42);
animal_counts(a_flower);
animal_counts(a_computer);
animal_counts(the_platonic_ideal_of_a_chair);
اینها ممکن است کامپایل شوند و کاری انجام دهند یا ممکن است به دلیل اجرای templateئه کاری که Typeها از آن پشتیبانی نمیکنند، موفق به کامپایل نشوند.
Constrained Auto:
در حالت ایدهآل، ما هم خدا را میخواهیم و هم خرما را! یعنی اینکه هم میخواهیم رابط این الگوی تابع را با Typeهایی که پشتیبانی میشود مستند کنیم و هم به کامپایلر این امکان را بدهیم که وقتی declaration ما با type ناسازگاری نمونهسازی میشود، خطاهای دقیق بدهد.
اینجاست که C++20 به ما Conceptها را میدهد تا برای حل این مشکل به ما کمک کند.
اگر animal conceptای داشته باشیم که مشخص میکند یک type که یک Animal را نشان میدهد چه interfaceای باید داشته باشد، میتوانیم از آن به اینصورت استفاده کنیم:
template <animal Animal>
void animal_counts(Animal const& the_animal);
خب این کار کاملا کوتاه و البته تکراری است. در حالت ایدهآل، میتوانیم از نام concept بهصورت مستقیم در function parameter list استفاده کنیم:
void animal_counts(animal const& the_animal);
با این حال، این نحو از استانداردسازی رد شد، چون نمیتوانید تشخیص دهید که آیا این یک function template است یا یک فانکشن معمولی، بدون اینکه بدانید animal یک type است یا یک concept.
خوشبختانه نسخهای از این نحو در C++20 قرار گرفت که دوباره از کلمهکلیدی auto استفاده میکند:
void animal_counts(animal auto const& the_animal);
این بررسی میکند که هرچیزی که جایگزین auto شود، خواستهی animal concept را برآورده میکند. بنابراین اگر template را با یک بچهگربه instantiate کنیم، :
animal<kitten>
بررسی میشود. این کار، یک سینتکس مختصر را به ما بازمیگرداند و در عین حال به ما اجازه میدهد تا declarationهای template خودمان را محدود کنیم. هم خدا را برآورده کردیم و هم خرما را!
#Cpp20 #Concept #Function_Templates2 130
متأسفانه یکی از بدبختیهای زبان سیپلاسپلاس، مشکل Undefined Behavior هست. مثلاً این کد در نظر داشته باشید:
https://t.me/cppFarsi/280
که خب واقعاً کار رو برای Debug کردن سخت میکنه. امّا امروز به یک موردی برخوردم که تقریباً حل کردنش یه کم راحتتر بود، سوأل کامل رو اینجا پرسیدم:
https://stackoverflow.com/questions/70821399/is-undefined-behavior-to-using-invalid-iterator
یه همچین کدی رو در نظر داشته باشید:
#include <map>
int main()
{
std::map<int, int> map;
map.find(1)->second;
}
این کد میتونه بدون هیچ مشکلی کار بکنه و حتیٰ Result اشتباه به شما بده؛ در حالی که انتظار دارید با Dereference کردن یک Invalid Iterator برنامهٔ شما Segmentation Fault بگیره. امّا این اتفاق نمیافته، چرا که اینکار باعث Undefined Behavior میشه که خب ... کار میکنه و Debug کردنش رو مشکل.
برای اینکه بتونید این خطاها رو از پیادهسازی ++libstdc خواهش بکنید که برای شما بگیره، میتونید برنامهاتون رو با ماکروی:
_GLIBCXX_DEBUGـ Compile بکنید. البته منطقاً این فقط برای glibc جواب میده و برای پیادهسازی دیگه باید مستنداتش رو بررسی بکنید. با استفاده از این ماکرو، بعد از اجرای کد بالا، یه همچین خطایی میگیرید:
Error: attempt to dereference a past-the-end iterator.(البته خیلی کاملتر توضیح داده، من اینجا خلاصه کردم). #ub #trick #iterator #glibc
2 130
#قوانین
به گروه زبانهای برنامهنویسی سی/سیپلاسپلاس خوشآمدید، در این گروه ما به بحث/پرسش/پاسخ در حیطهٔ آن دو زبان برنامهنویسی میپردازیم. برای شروع بحث/پرسش/پاسخ، لطفاً مطمئن شوید که این قوانین را نقض نکرده باشید. درصورت نقض قوانین، مدیران گروه بنا به صلاحدید اقدام به برخورد با شخص خاطی خواهند کرد.
۱- احترام را در پیامهای خود رعایت بکنید.
۲- قبل از پرسیدن سوأل، مطمئن شوید که سوألات قبلاً در Stackoverflow پاسخ داده نشده باشد و حداقل در اوّلین نتیجهٔ جستجو در Google ظاهر نشود.
۳- سعی کنید سوألاتان استانداردهای یک سوأل خوب را رعایت کرده باشد:
https://wiki.ubuntu.ir/wiki/Smart_Questions
۴- پیامهایی که مربط با گروه نیستند، همراه با ارسال کنندهٔ آن پیام، از گروه حذف میشوند.
۵- پیامتان را کاملاً در یک پست ارسال بکنید، از ارسال چندین پست برای یک پیام خودداری کنید.
۶- به هیچ عنوان کدهای طولانی داخل گروه ارسال نکنید و انتظار Debug کردنش را از افراد داشته باشید. کدهایتان را داخل یک سرویس Pastebin ارسال بکنید.
۷- به هیچ عنوان کل پروژه و یا تیکهای از کدتان که خطا دارد را ارسال نکنید، همیشه سعی در درست کردن یک نمونهٔ کوچک قابل ارسال از مشکلات کنید.
۸- درخواست حل تمرین بدون هیچ اخطاری از سمت مدیران، همراه با ارسال کنندهٔ درخواست، حذف میشود.
