Exercises — Modules (C++20) — concept and syntax
5.2.32 · D4· Coding › C++ Programming › Modules (C++20) — concept and syntax
Shuru karne se pehle, ek shared mental picture jo har exercise mein kaam aati hai. Module ko ek sealed box ki tarah socho. Box ka lid un names ki list hoti hai jo outsiders reach karke touch kar sakte hain (woh exported names hain). Baaki sab andar sealed hai. import tumhe lid ki list deta hai; woh tumhe kabhi box ke andar rummage karne nahi deta.
Neeche diya figure dekho aur poore page ke liye ise dimag mein rakhna. Left mein module math ek box ki tarah bana hai: yellow lid line (export int add(...)) sirf yahi cheez box se bahar jaati hai; red items (secret_helper, aur quarantined #include) andar sealed hain. Right mein, main.cpp (green) import math; likhta hai aur sirf wahi receive karta hai jo lid par hai. Yellow arrow exported name ko boundary cross karte dikhata hai; dashed red arrow ek private name ko block hote dikhata hai. Is page ka har problem actually yahi question hai ki tum kaunsa arrow draw kar rahe ho.

Figure description (unke liye jo image nahi dekh sakte): do boxes side by side hain. Left box, blue outline mein, "module math" label hai. Uske andar, yellow mein, LID list hai jisme sirf ek exported line hai export int add(...); uske neeche, red mein, SEALED INSIDE list hai jisme int secret_helper() aur #include <cmath> (fragment) hain. Right box, green outline mein, "main.cpp" label hai aur usme import math; aur working call add(2,3) OK hai. Ek solid yellow arrow yellow lid line se main.cpp tak jaata hai, "crosses" mark hai. Ek dashed red arrow red private items se main.cpp ki taraf jaata hai lekin "blocked" mark hai.
Level 1 — Recognition
Goal: sahi keyword line spot karo aur file ka role naam karo.
L1.1
Tum ek file kholte ho jiska pehla real line yeh hai:
export module math;Is file ko kya kehte hain, aur ek module mein aise kitni files ho sakti hain?
Recall Solution
Yeh module math ka primary module interface unit hai. Ek module mein exactly ek aisi unit hoti hai. Iske export kiye hue names module ka public face hain — box lid ki list (figure mein yellow line).
Exactly ek kyun? Primary interface unit hi module ki public surface ki definition hai; agar do files dono yeh claim karti, to compiler ke paas same surface ki do conflicting definitions hoti — isliye language ne yeh forbid kiya hai.
Answer key: role = primary interface unit, count = 1.
L1.2
In first-lines mein se har ek ko classify karo — interface unit, implementation unit, partition interface unit, ya global module fragment start:
(a) module;
(b) export module math;
(c) module math;
(d) export module math:geometry;Recall Solution
(a) module;— global module fragment start karta hai (#includeke liye quarantine room, upar box mein define kiya).(b) export module math;— primary interface unit.(c) module math;— implementation unit (koiexportnahi, koi naya public name nahi add karta).(d) export module math:geometry;— ek partition interface unit (dekho Module Partitions).
L1.3
Kaunsi line module math ke sirf exported names tumhari file mein laati hai, aur kaunsi ek file ka content textually paste karti hai?
Recall Solution
import math;→ sirf exported surface laata hai (lid ki list, figure mein yellow arrow). Koi pasting nahi.#include "math.h"→ textual copy-paste jo The C++ Preprocessor handle karta hai.
Level 2 — Application
Goal: sahi module syntax likho aur predict karo kya compile hoga.
L2.1
Module strutil ke liye ek primary interface unit likho jo function int length(const char* s) export kare lekin helper bool is_null(const char* s) ko module ke liye private rakhe.
Recall Solution
export module strutil;
bool is_null(const char* s) { // NO export => private to the module
return s == nullptr;
}
export int length(const char* s) { // exported => visible to importers
if (is_null(s)) return 0;
int n = 0;
while (s[n] != '\0') ++n;
return n;
}Kyun yeh compile hota hai aur sahi hai: export module strutil; pehla real declaration hai, isliye yeh file legally primary interface unit hai. is_null mein koi export nahi, isliye module rule ke hisaab se woh kabhi box boundary cross nahi karta — woh module ke liye private hai. length mein export hai, isliye iska naam lid par rakha jaata hai. Khas baat yeh hai ki length phir bhi is_null ko call kar sakta hai kyunki dono ek hi module box ke andar rehte hain; "private" sirf outsiders ko block karta hai, module ke fellow members ko kabhi nahi. Toh code both legal hai aur wahi karta hai jo problem ne manga.
L2.2
Yeh file compile karne mein fail hoti hai. Woh single rule dhundho jo yeh todta hai aur fix karo.
export module geometry;
#include <cmath>
export double norm(double a, double b) {
return std::sqrt(a*a + b*b);
}Recall Solution
Rule toda: #include named-module region shuru hone ke baad appear nahi ho sakta. #include global module fragment (quarantine room) mein rehna chahiye, export module ...; se pehle.
Fixed:
module; // global module fragment starts
#include <cmath> // legacy header allowed ONLY here
export module geometry;
export double norm(double a, double b) {
return std::sqrt(a*a + b*b);
}Kyun fix legal hai: module; fragment open karta hai; #include <cmath> ab sirf us region mein baithta hai jo preprocessor includes allow karta hai; phir export module geometry; clean named region shuru karta hai, jiske baad exported declarations allowed hain. Har line ab us region mein hai jo language us ke liye require karti hai.
L2.3
Yeh module given hai:
export module counter;
export int increment(int x) { return x + 1; }
int seed() { return 100; }aur yeh consumer:
import counter;
int main() {
int a = increment(5); // line A
int b = seed(); // line B
return a + b;
}Kaunsi line fail hoti hai, aur program would compute kya karta agar sirf legal call run hoti (yaani a ki value)?
Recall Solution
Line B fail hoti hai — seed kabhi export nahi hua, isliye woh module boundary ke across invisible hai (figure mein dashed red arrow).
Kyun line A legal hai: increment mein export hai, isliye iska naam lid par hai aur import counter; use main ki view mein rakhta hai. Line A isliye compile hoti hai aur a = increment(5) = 6.
Level 3 — Analysis
Goal: interface vs implementation ke baare mein reason karo, aur build cost ke baare mein.
L3.1
Tum module account ko do files mein split karte ho:
// account.ixx
export module account;
export class Account {
public:
Account(double b);
double balance() const;
private:
double bal_;
};// account.cpp
module account;
Account::Account(double b) : bal_(b) {}
double Account::balance() const { return bal_; }Implementation file account.cpp module account; kehti hai bina export ke. Bilkul precisely explain karo ki yeh public interface mein kuch add nahi karta, phir bhi Account::Account define karne ki allowed kyun hai.
Recall Solution
module account; account.cpp ko module account ka implementation unit mark karta hai. Implementation units:
- module ke internals share karti hain (woh sab kuch dekh sakti hain jo interface ne declare kiya, isliye
Accountyahan ek known type hai), aur - naye names export nahi kar sakti — module-declaration level par
exportkeyword allowed nahi hai.
Isliye Account::Account aur Account::balance ki definitions already-declared members ki implementations hain, naye public names nahi. Public face single interface unit ne fix kar diya tha. Yeh interface files chhoti rakhta hai (importers ke liye parse karna fast) jabki heavy definitions alag se recompile hoti hain — yeh Build Systems and Compilation Speed ka fayda hai.
L3.2
Ek header <vector> 100 translation units mein #include hoti hai. Modules ke saath tum <vector> ko ek baar reusable interface ki tarah build karte ho (chahe header unit ho ya module) aur reuse karte ho. Mano ki vector interface text parse/compile karne ka cost har parse ke liye ek unit kaam hai. Dono strategies ke liye total parse-work estimate karo (raw interface parses count karo). Yeh comparison is section ke figure mein draw kiya gaya hai.
Recall Solution
#include(textual paste): interface text har translation unit mein re-parse hoti hai, isliye cost hai 100 times 1, yaani 100 units.- compiled interface (ek baar parse, reuse): ek baar 1 unit mein parse/compile hoti hai binary interface mein, phir reuse hoti hai, isliye cost hai 1 unit.
Yahan speedup factor hai 100 divided by 1, yaani 100 times. General rule: N importers ke liye, #include cost N ke proportional hai, import cost us interface ke liye 1 ke proportional hai.
Compiler bina modules ke header work cache kyun nahi kar sakta? Har translation unit ek independent program run ki tarah compile hoti hai, aur #include pure text substitution hai jo compiler ek "unit" dekhne se pehle hi kar deta hai — dekho Translation Units and Linkage aur The C++ Preprocessor. Paste ki gayi header text alag-alag files mein alag alag cheez mean kar sakti hai, kyunki #include line se pehle define kiye macros header ke parse hone ka tarika badal sakte hain. Isliye compiler yeh assume karne ki allowed nahi ki file A mein <vector> ka parse file B ke parse ke barabar hai — use ise redo karna hi padta hai. Ek module interface woh ambiguity remove karta hai: woh ek fixed, self-contained context mein ek binary artifact ke roop mein compile hoti hai, jo har importer ke liye provably same hai aur isliye safely reusable hai. Woh provable sameness — raw speed nahi — wahi hai jo caching unlock karta hai.

Figure description (unke liye jo image nahi dekh sakte): ek grouped bar chart hai. Horizontal axis char cases list karti hai, N=10, N=50, N=100, N=200, yaani interface import karne wale translation units ki sankhya. Har case ke liye do bars side by side khade hain. Red bars, "#include (O(N) parses)" label ke saath, N ke barabar heights hain, isliye woh N badne ke saath bade hote jaate hain (10, 50, 100, 200). Green bars, "import (O(1) parse)" label ke saath, har case mein height 1 par flat rehte hain. Flat green bar visually dikhata hai ki import cost N ke saath nahi badhti, jabki rising red bars dikhate hain ki #include cost linearly N ke saath scale karti hai.
Upar ka bar chart scaling ko literal bana deta hai: red #include bar translation units N ki sankhya ke saath badhta hai, jabki green import bar N ki parwah kiye bina 1 par flat rehta hai. Woh flat green bar hi woh poora reason hai ki modules large builds ko speed up karte hain — dekho Build Systems and Compilation Speed.
L3.3
In chaar lines ko sirf ek legal sequence mein order karo ek aise module ke liye jo ek legacy header chahta hai, phir har position justify karo:
export module m;
module;
export void f();
#include <cstdio>
Recall Solution
Legal order:
module; // (1) global module fragment open karta hai
#include <cstdio> // (2) legacy include — sirf fragment mein allowed
export module m; // (3) named-module region shuru karta hai
export void f(); // (4) ek exported declaration, region shuru hone ke baadJustification: #include sirf global module fragment mein legal hai, jo export module m; se pehle aana chahiye; aur exported declarations sirf export module m; ke baad legal hain.
Level 4 — Synthesis
Goal: partitions, implementation units, aur quarantine ko sahi se combine karo.
L4.1
Module math bada ho raha hai. Tum ek public sub-piece math:trig chahte ho jo apni file mein rahe lekin phir bhi module math ka hissa ho. Partition interface unit likho aur dikhao ki primary interface use kaise re-export karta hai taki import math; akele users ko trig names mile.
Recall Solution
Partition file (math-trig.ixx):
export module math:trig;
export double sine(double x); // is partition mein declaredPrimary interface (math.ixx):
export module math;
export import :trig; // partition re-export karo taki importers uske names dekheinKyun yeh legal hai aur kaam karta hai: export module math:trig; math ka ek partition name karta hai, naya module nahi — colon syntax ise parent module se tie karta hai, isliye yeh doosra primary interface nahi hai (jo illegal hota). Primary interface phir export import :trig; likhta hai, jo partition ko import bhi karta hai aur uske names re-export bhi karta hai, unhe math ki apni lid par rakhta hai. Nateeje mein import math; likhne wala consumer sine bhi receive karta hai. Dekho Module Partitions. Abhi bhi exactly ek primary interface unit hai; partitions internal sub-units hain.
L4.2
Module geometry design karo jo (a) internally <cmath> chahta hai, (b) ek class Circle export kare jisme ek public method area() aur ek private radius ho, aur (c) area() ki definition ek alag implementation unit mein rakhe. Teeno files likho.
Recall Solution
Global-fragment + interface (geometry.ixx):
module;
#include <cmath> // quarantined legacy header
export module geometry;
export class Circle {
public:
explicit Circle(double r);
double area() const; // yahan declare, impl unit mein define
private:
double r_; // private: callers se chhupta hai
};Implementation unit (geometry.cpp):
module geometry; // koi 'export' nahi — koi naya public name nahi
Circle::Circle(double r) : r_(r) {}
double Circle::area() const {
return 3.141592653589793 * r_ * r_;
}Consumer (main.cpp):
import geometry;
#include <iostream>
int main() {
Circle c(2.0);
std::cout << c.area(); // legal: area() public hai aur Circle exported
// c.r_; // ERROR: r_ private hai
}Kyun har piece legal hai: geometry.ixx mein, #include <cmath> global module fragment mein baithta hai (sirf yahi jagah allowed hai), export module geometry; se pehle; class export hai isliye iska naam importers tak pahunchta hai. geometry.cpp mein, module geometry; (koi export nahi) ise ek implementation unit banata hai jo already-declared members define kar sakti hai bina naye public names add kiye. main.cpp mein, c.area() compile hota hai kyunki Circle export hua tha (module boundary cross) aur area() public hai (class boundary cross); c.r_ fail hota kyunki r_ private hai — class-access filter use block karta hai chahe module filter pass kar gaya.
Numeric answer: Circle c(2.0) ke liye, area() return karta hai pi times 2 squared, yaani 4 pi, approximately 12.566370614.
Level 5 — Mastery
Goal: macro boundary aur pure build mein behaviour predict karo.
Is level se pehle ek baar header unit idea yaad karo, kyunki L5.1 isi par turn karta hai: header unit ek header hai jise import "config.h"; se #include "config.h"; ki jagah pull in kiya jaata hai. Compiler ise module ki tarah ek baar build karta hai (parse-once speed), lekin kyunki yeh abhi bhi header se bana hai, yeh header ke macros preserve karta hai — normal module import se alag, jo kabhi macros export nahi karta. Isliye header unit woh tool hai jo tum tab use karte ho jab tumhe speed aur shared macro dono chahiye.
L5.1
// config.ixx
export module config;
#define MAX 256 // (mano ki yeh module region ke andar hai)
export int cap() { return 100; }// main.cpp
import config;
int main() {
return cap() + MAX; // kya MAX yahan resolve hoga?
}Kya MAX main.cpp mein resolve hoga? Explain karo, aur woh alternative bhi do jo macro share karta.
Recall Solution
MAX resolve nahi hoga main.cpp mein. Kyun: import deliberately macros module boundary ke across nahi le jaata — yahi woh exact leakage cure hai jiske liye modules design kiye gaye the (contrast karo The C++ Preprocessor ke paste behaviour se). Isliye MAX main.cpp mein unknown hai aur woh file MAX token par compile karne mein fail hoti hai.
Exported function theek hai: cap lid par hai, isliye cap() = 100 visible hai.
Alternative jo macro share kare: ek header unit use karo (is level se thoda upar define kiya):
// MAX ko config.h mein rakho, phir main.cpp mein:
import "config.h"; // header unit — macros PRESERVE karta haiKyun alternative kaam karta hai: ek header unit abhi bhi header se bana hai, isliye uska #define MAX 256 importer tak survive karta hai, jabki woh module ki parse-once speed bhi enjoy karta hai. Yeh module system ke under shared macro ka ek legitimate raasta hai.
L5.2
Ek project mein module bignum ke liye ek interface unit hai, jise N = 250 translation units import karti hain. #include-style headers ke saath har translation unit bignum interface re-parse karti thi (cost 1 each). #include aur import ke under total interface-parse cost compute karo, aur is interface ke liye speedup ratio.
Recall Solution
#include: 250 times 1, yaani 250 parse-units.import: 1 parse-unit (ek baar compile, reuse).- Speedup ratio hai 250 divided by 1, yaani 250 times.
Kyun yeh ek par aa jaata hai: jaise L3.2 mein argue kiya, module interface ek single binary artifact mein compile hoti hai jo har importer ke liye provably identical hai, isliye 250 re-parses jo #include karne par majboor tha woh ek single reuse ban jaate hain. Yeh Translation Units and Linkage insight quantitative ho gayi: cost scale karta hai kitni baar same interface text parse hoti hai ke saath, aur modules use ek par collapse kar dete hain.
L5.3 (capstone)
Yeh legal module given hai:
export module bank;
export int fee() { return 5; }
int audit() { return 3; } // private
export int total(int deposit) {
return deposit - fee() - audit();
}aur consumer:
import bank;
int main() {
return total(20) + fee(); // returned value compute karo
}Program ka return value compute karo, aur batao ki kya program abhi bhi compile hota agar tum total(20) ko total(20) + audit() se replace kar dete.
Recall Solution
Module ke andar, total(20) = 20 - fee() - audit() = 20 - 5 - 3 = 12. Kyun total, audit call kar sakta hai: woh dono ek hi module box ke andar rehte hain, aur private sirf outsiders ko block karta hai — isliye yeh internal call legal hai.
main mein: total(20) + fee() = 12 + 5 = 17.
Agar tum main mein total(20) + audit() likhte, toh yeh compile karne mein fail hota — audit kabhi export nahi hua, isliye woh module boundary ke across invisible hai (phir wahi dashed red arrow). (Number hota 12 + 3 = 15 agar compile hota, lekin hota nahi.)
Active recall
Recall Rapid self-test (expand se pehle jawab do)
- Ek module mein kitne primary interface units? → Exactly ek.
- Module ke andar
#includelegally kahan baithta hai? → Global module fragment (beforeexport module). - Kya
import(kisi module ka) macros laata hai? → Nahi — lekin header unit laata hai. - Interface-parse cost scaling: N importers ke liye
#includevsimport? → order N vs constant. - Ek file ko implementation unit kya banata hai? →
module Name;binaexportke.
Module bank ke andar total(20) ki value?
main mein total(20) + fee() ka return value?
250 importers ke liye speedup ratio, #include vs import?
Circle(2.0).area() ki value pi times r squared ke saath?
Header unit kya hai, aur use kyun karte hain?
import "config.h";) — module ki tarah ek baar parse hota hai lekin macros preserve karta hai, isliye yeh macro share karne ka tarika hai.